diff options
Diffstat (limited to 'drivers/pmdomain')
30 files changed, 1783 insertions, 220 deletions
diff --git a/drivers/pmdomain/amlogic/meson-ee-pwrc.c b/drivers/pmdomain/amlogic/meson-ee-pwrc.c index fbb2b4103930..55c8c9f66a1b 100644 --- a/drivers/pmdomain/amlogic/meson-ee-pwrc.c +++ b/drivers/pmdomain/amlogic/meson-ee-pwrc.c @@ -69,27 +69,27 @@ struct meson_ee_pwrc_domain_desc { char *name; unsigned int reset_names_count; unsigned int clk_names_count; - struct meson_ee_pwrc_top_domain *top_pd; + const struct meson_ee_pwrc_top_domain *top_pd; unsigned int mem_pd_count; - struct meson_ee_pwrc_mem_domain *mem_pd; + const struct meson_ee_pwrc_mem_domain *mem_pd; bool (*is_powered_off)(struct meson_ee_pwrc_domain *pwrc_domain); }; struct meson_ee_pwrc_domain_data { unsigned int count; - struct meson_ee_pwrc_domain_desc *domains; + const struct meson_ee_pwrc_domain_desc *domains; }; /* TOP Power Domains */ -static struct meson_ee_pwrc_top_domain gx_pwrc_vpu = { +static const struct meson_ee_pwrc_top_domain gx_pwrc_vpu = { .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, .sleep_mask = BIT(8), .iso_reg = GX_AO_RTI_GEN_PWR_SLEEP0, .iso_mask = BIT(9), }; -static struct meson_ee_pwrc_top_domain meson8_pwrc_vpu = { +static const struct meson_ee_pwrc_top_domain meson8_pwrc_vpu = { .sleep_reg = MESON8_AO_RTI_GEN_PWR_SLEEP0, .sleep_mask = BIT(8), .iso_reg = MESON8_AO_RTI_GEN_PWR_SLEEP0, @@ -104,20 +104,20 @@ static struct meson_ee_pwrc_top_domain meson8_pwrc_vpu = { .iso_mask = BIT(__bit), \ } -static struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8); -static struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16); -static struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17); -static struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18); -static struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19); +static const struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8); +static const struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16); +static const struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17); +static const struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18); +static const struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19); -static struct meson_ee_pwrc_top_domain g12a_pwrc_nna = { +static const struct meson_ee_pwrc_top_domain g12a_pwrc_nna = { .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, .sleep_mask = BIT(16) | BIT(17), .iso_reg = GX_AO_RTI_GEN_PWR_ISO0, .iso_mask = BIT(16) | BIT(17), }; -static struct meson_ee_pwrc_top_domain g12a_pwrc_isp = { +static const struct meson_ee_pwrc_top_domain g12a_pwrc_isp = { .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, .sleep_mask = BIT(18) | BIT(19), .iso_reg = GX_AO_RTI_GEN_PWR_ISO0, @@ -154,39 +154,39 @@ static struct meson_ee_pwrc_top_domain g12a_pwrc_isp = { { __reg, BIT(14) }, \ { __reg, BIT(15) } -static struct meson_ee_pwrc_mem_domain axg_pwrc_mem_vpu[] = { +static const struct meson_ee_pwrc_mem_domain axg_pwrc_mem_vpu[] = { VPU_MEMPD(HHI_VPU_MEM_PD_REG0), VPU_HHI_MEMPD(HHI_MEM_PD_REG0), }; -static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = { +static const struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = { VPU_MEMPD(HHI_VPU_MEM_PD_REG0), VPU_MEMPD(HHI_VPU_MEM_PD_REG1), VPU_MEMPD(HHI_VPU_MEM_PD_REG2), VPU_HHI_MEMPD(HHI_MEM_PD_REG0), }; -static struct meson_ee_pwrc_mem_domain gxbb_pwrc_mem_vpu[] = { +static const struct meson_ee_pwrc_mem_domain gxbb_pwrc_mem_vpu[] = { VPU_MEMPD(HHI_VPU_MEM_PD_REG0), VPU_MEMPD(HHI_VPU_MEM_PD_REG1), VPU_HHI_MEMPD(HHI_MEM_PD_REG0), }; -static struct meson_ee_pwrc_mem_domain meson_pwrc_mem_eth[] = { +static const struct meson_ee_pwrc_mem_domain meson_pwrc_mem_eth[] = { { HHI_MEM_PD_REG0, GENMASK(3, 2) }, }; -static struct meson_ee_pwrc_mem_domain meson8_pwrc_audio_dsp_mem[] = { +static const struct meson_ee_pwrc_mem_domain meson8_pwrc_audio_dsp_mem[] = { { HHI_MEM_PD_REG0, GENMASK(1, 0) }, }; -static struct meson_ee_pwrc_mem_domain meson8_pwrc_mem_vpu[] = { +static const struct meson_ee_pwrc_mem_domain meson8_pwrc_mem_vpu[] = { VPU_MEMPD(HHI_VPU_MEM_PD_REG0), VPU_MEMPD(HHI_VPU_MEM_PD_REG1), VPU_HHI_MEMPD(HHI_MEM_PD_REG0), }; -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = { +static const struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = { VPU_MEMPD(HHI_VPU_MEM_PD_REG0), VPU_MEMPD(HHI_VPU_MEM_PD_REG1), VPU_MEMPD(HHI_VPU_MEM_PD_REG2), @@ -198,28 +198,28 @@ static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = { VPU_HHI_MEMPD(HHI_MEM_PD_REG0), }; -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = { +static const struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = { { HHI_NANOQ_MEM_PD_REG0, 0xff }, { HHI_NANOQ_MEM_PD_REG1, 0xff }, }; -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = { +static const struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = { { HHI_MEM_PD_REG0, GENMASK(31, 30) }, }; -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = { +static const struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = { { HHI_MEM_PD_REG0, GENMASK(29, 26) }, }; -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = { +static const struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = { { HHI_MEM_PD_REG0, GENMASK(25, 18) }, }; -static struct meson_ee_pwrc_mem_domain axg_pwrc_mem_audio[] = { +static const struct meson_ee_pwrc_mem_domain axg_pwrc_mem_audio[] = { { HHI_MEM_PD_REG0, GENMASK(5, 4) }, }; -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = { +static const struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = { { HHI_MEM_PD_REG0, GENMASK(5, 4) }, { HHI_AUDIO_MEM_PD_REG0, GENMASK(1, 0) }, { HHI_AUDIO_MEM_PD_REG0, GENMASK(3, 2) }, @@ -235,12 +235,12 @@ static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = { { HHI_AUDIO_MEM_PD_REG0, GENMASK(27, 26) }, }; -static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_nna[] = { +static const struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_nna[] = { { G12A_HHI_NANOQ_MEM_PD_REG0, GENMASK(31, 0) }, { G12A_HHI_NANOQ_MEM_PD_REG1, GENMASK(31, 0) }, }; -static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_isp[] = { +static const struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_isp[] = { { G12A_HHI_ISP_MEM_PD_REG0, GENMASK(31, 0) }, { G12A_HHI_ISP_MEM_PD_REG1, GENMASK(31, 0) }, }; @@ -270,14 +270,14 @@ static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_isp[] = { static bool pwrc_ee_is_powered_off(struct meson_ee_pwrc_domain *pwrc_domain); -static struct meson_ee_pwrc_domain_desc axg_pwrc_domains[] = { +static const struct meson_ee_pwrc_domain_desc axg_pwrc_domains[] = { [PWRC_AXG_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, axg_pwrc_mem_vpu, pwrc_ee_is_powered_off, 5, 2), [PWRC_AXG_ETHERNET_MEM_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), [PWRC_AXG_AUDIO_ID] = MEM_PD("AUDIO", axg_pwrc_mem_audio), }; -static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = { +static const struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = { [PWRC_G12A_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, g12a_pwrc_mem_vpu, pwrc_ee_is_powered_off, 11, 2), [PWRC_G12A_ETH_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), @@ -287,13 +287,13 @@ static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = { pwrc_ee_is_powered_off), }; -static struct meson_ee_pwrc_domain_desc gxbb_pwrc_domains[] = { +static const struct meson_ee_pwrc_domain_desc gxbb_pwrc_domains[] = { [PWRC_GXBB_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, gxbb_pwrc_mem_vpu, pwrc_ee_is_powered_off, 12, 2), [PWRC_GXBB_ETHERNET_MEM_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), }; -static struct meson_ee_pwrc_domain_desc meson8_pwrc_domains[] = { +static const struct meson_ee_pwrc_domain_desc meson8_pwrc_domains[] = { [PWRC_MESON8_VPU_ID] = VPU_PD("VPU", &meson8_pwrc_vpu, meson8_pwrc_mem_vpu, pwrc_ee_is_powered_off, 0, 1), @@ -303,7 +303,7 @@ static struct meson_ee_pwrc_domain_desc meson8_pwrc_domains[] = { meson8_pwrc_audio_dsp_mem), }; -static struct meson_ee_pwrc_domain_desc meson8b_pwrc_domains[] = { +static const struct meson_ee_pwrc_domain_desc meson8b_pwrc_domains[] = { [PWRC_MESON8_VPU_ID] = VPU_PD("VPU", &meson8_pwrc_vpu, meson8_pwrc_mem_vpu, pwrc_ee_is_powered_off, 11, 1), @@ -313,7 +313,7 @@ static struct meson_ee_pwrc_domain_desc meson8b_pwrc_domains[] = { meson8_pwrc_audio_dsp_mem), }; -static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = { +static const struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = { [PWRC_SM1_VPU_ID] = VPU_PD("VPU", &sm1_pwrc_vpu, sm1_pwrc_mem_vpu, pwrc_ee_is_powered_off, 11, 2), [PWRC_SM1_NNA_ID] = TOP_PD("NNA", &sm1_pwrc_nna, sm1_pwrc_mem_nna, @@ -576,32 +576,32 @@ static void meson_ee_pwrc_shutdown(struct platform_device *pdev) } } -static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = { +static const struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = { .count = ARRAY_SIZE(g12a_pwrc_domains), .domains = g12a_pwrc_domains, }; -static struct meson_ee_pwrc_domain_data meson_ee_axg_pwrc_data = { +static const struct meson_ee_pwrc_domain_data meson_ee_axg_pwrc_data = { .count = ARRAY_SIZE(axg_pwrc_domains), .domains = axg_pwrc_domains, }; -static struct meson_ee_pwrc_domain_data meson_ee_gxbb_pwrc_data = { +static const struct meson_ee_pwrc_domain_data meson_ee_gxbb_pwrc_data = { .count = ARRAY_SIZE(gxbb_pwrc_domains), .domains = gxbb_pwrc_domains, }; -static struct meson_ee_pwrc_domain_data meson_ee_m8_pwrc_data = { +static const struct meson_ee_pwrc_domain_data meson_ee_m8_pwrc_data = { .count = ARRAY_SIZE(meson8_pwrc_domains), .domains = meson8_pwrc_domains, }; -static struct meson_ee_pwrc_domain_data meson_ee_m8b_pwrc_data = { +static const struct meson_ee_pwrc_domain_data meson_ee_m8b_pwrc_data = { .count = ARRAY_SIZE(meson8b_pwrc_domains), .domains = meson8b_pwrc_domains, }; -static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = { +static const struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = { .count = ARRAY_SIZE(sm1_pwrc_domains), .domains = sm1_pwrc_domains, }; 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/Kconfig b/drivers/pmdomain/arm/Kconfig index efa139c34e08..afed10d382ad 100644 --- a/drivers/pmdomain/arm/Kconfig +++ b/drivers/pmdomain/arm/Kconfig @@ -2,7 +2,7 @@ config ARM_SCMI_PERF_DOMAIN tristate "SCMI performance domain driver" depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF) - default y + default ARM_SCMI_PROTOCOL select PM_GENERIC_DOMAINS if PM help This enables support for the SCMI performance domains which can be @@ -14,7 +14,7 @@ config ARM_SCMI_PERF_DOMAIN config ARM_SCMI_POWER_DOMAIN tristate "SCMI power domain driver" depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF) - default y + default ARM_SCMI_PROTOCOL select PM_GENERIC_DOMAINS if PM help This enables support for the SCMI power domains which can be @@ -27,7 +27,7 @@ config ARM_SCMI_POWER_DOMAIN config ARM_SCPI_POWER_DOMAIN tristate "SCPI power domain driver" depends on ARM_SCPI_PROTOCOL || (COMPILE_TEST && OF) - default y + default ARM_SCPI_PROTOCOL select PM_GENERIC_DOMAINS if PM help This enables support for the SCPI power domains which can be 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/bcm/bcm2835-power.c b/drivers/pmdomain/bcm/bcm2835-power.c index d3cd816979ac..f5289fd184d0 100644 --- a/drivers/pmdomain/bcm/bcm2835-power.c +++ b/drivers/pmdomain/bcm/bcm2835-power.c @@ -506,18 +506,10 @@ bcm2835_init_power_domain(struct bcm2835_power *power, struct device *dev = power->dev; struct bcm2835_power_domain *dom = &power->domains[pd_xlate_index]; - dom->clk = devm_clk_get(dev->parent, name); - if (IS_ERR(dom->clk)) { - int ret = PTR_ERR(dom->clk); - - if (ret == -EPROBE_DEFER) - return ret; - - /* Some domains don't have a clk, so make sure that we - * don't deref an error pointer later. - */ - dom->clk = NULL; - } + dom->clk = devm_clk_get_optional(dev->parent, name); + if (IS_ERR(dom->clk)) + return dev_err_probe(dev, PTR_ERR(dom->clk), "Failed to get clock %s\n", + name); dom->base.name = name; dom->base.flags = GENPD_FLAG_ACTIVE_WAKEUP; diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index d6c1ddb807b2..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) @@ -304,10 +315,40 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd) genpd->accounting_time = now; } + +static void genpd_reflect_residency(struct generic_pm_domain *genpd) +{ + struct genpd_governor_data *gd = genpd->gd; + struct genpd_power_state *state, *next_state; + unsigned int state_idx; + s64 sleep_ns, target_ns; + + if (!gd || !gd->reflect_residency) + return; + + sleep_ns = ktime_to_ns(ktime_sub(ktime_get(), gd->last_enter)); + state_idx = genpd->state_idx; + state = &genpd->states[state_idx]; + target_ns = state->power_off_latency_ns + state->residency_ns; + + if (sleep_ns < target_ns) { + state->above++; + } else if (state_idx < (genpd->state_count -1)) { + next_state = &genpd->states[state_idx + 1]; + target_ns = next_state->power_off_latency_ns + + next_state->residency_ns; + + if (sleep_ns >= target_ns) + state->below++; + } + + gd->reflect_residency = false; +} #else static inline void genpd_debug_add(struct generic_pm_domain *genpd) {} static inline void genpd_debug_remove(struct generic_pm_domain *genpd) {} static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {} +static inline void genpd_reflect_residency(struct generic_pm_domain *genpd) {} #endif static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd, @@ -728,6 +769,64 @@ 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. + * @state_idx: The index of the idle-state that failed. + * + * In some special cases the ->power_off() callback is asynchronously powering + * off the PM domain, leading to that it may return zero to indicate success, + * even though the actual power-off could fail. To account for this correctly in + * the rejected/usage counts for the idle-state statistics, users can call this + * function to adjust the values. + * + * It is assumed that the users guarantee that the genpd doesn't get removed + * while this routine is getting called. + */ +void pm_genpd_inc_rejected(struct generic_pm_domain *genpd, + unsigned int state_idx) +{ + genpd_lock(genpd); + genpd->states[genpd->state_idx].rejected++; + genpd->states[genpd->state_idx].usage--; + genpd_unlock(genpd); +} +EXPORT_SYMBOL_GPL(pm_genpd_inc_rejected); + static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) { unsigned int state_idx = genpd->state_idx; @@ -853,31 +952,25 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) * If all of the @genpd's devices have been suspended and all of its subdomains * have been powered down, remove power from @genpd. */ -static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, - unsigned int depth) +static void genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, + unsigned int depth) { struct pm_domain_data *pdd; struct gpd_link *link; unsigned int not_suspended = 0; - int ret; /* * Do not try to power off the domain in the following situations: - * (1) The domain is already in the "power off" state. - * (2) System suspend is in progress. - */ - if (!genpd_status_on(genpd) || genpd->prepared_count > 0) - return 0; - - /* - * Abort power off for the PM domain in the following situations: - * (1) The domain is configured as always on. - * (2) When the domain has a subdomain being powered 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_is_always_on(genpd) || - genpd_is_rpm_always_on(genpd) || - atomic_read(&genpd->sd_count) > 0) - return -EBUSY; + if (!genpd_status_on(genpd) || genpd->prepared_count > 0 || + genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd) || + genpd->stay_on || atomic_read(&genpd->sd_count) > 0) + return; /* * The children must be in their deepest (powered-off) states to allow @@ -888,7 +981,7 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, list_for_each_entry(link, &genpd->parent_links, parent_node) { struct generic_pm_domain *child = link->child; if (child->state_idx < child->state_count - 1) - return -EBUSY; + return; } list_for_each_entry(pdd, &genpd->dev_list, list_node) { @@ -902,15 +995,15 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, /* The device may need its PM domain to stay powered on. */ if (to_gpd_data(pdd)->rpm_always_on) - return -EBUSY; + return; } if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on)) - return -EBUSY; + return; if (genpd->gov && genpd->gov->power_down_ok) { if (!genpd->gov->power_down_ok(&genpd->domain)) - return -EAGAIN; + return; } /* Default to shallowest state. */ @@ -919,12 +1012,11 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, /* Don't power off, if a child domain is waiting to power on. */ if (atomic_read(&genpd->sd_count) > 0) - return -EBUSY; + return; - ret = _genpd_power_off(genpd, true); - if (ret) { + if (_genpd_power_off(genpd, true)) { genpd->states[genpd->state_idx].rejected++; - return ret; + return; } genpd->status = GENPD_STATE_OFF; @@ -937,8 +1029,6 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, genpd_power_off(link->parent, false, depth + 1); genpd_unlock(link->parent); } - - return 0; } /** @@ -957,6 +1047,9 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) if (genpd_status_on(genpd)) return 0; + /* Reflect over the entered idle-states residency for debugfs. */ + genpd_reflect_residency(genpd); + /* * The list is guaranteed not to change while the loop below is being * executed, unless one of the parents' .power_on() callbacks fiddles @@ -1264,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) { @@ -1287,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 @@ -1450,7 +1549,7 @@ static int genpd_finish_suspend(struct device *dev, if (ret) return ret; - if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd)) + if (device_awake_path(dev) && genpd_is_active_wakeup(genpd)) return 0; if (genpd->dev_ops.stop && genpd->dev_ops.start && @@ -1505,7 +1604,7 @@ static int genpd_finish_resume(struct device *dev, if (IS_ERR(genpd)) return -EINVAL; - if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd)) + if (device_awake_path(dev) && genpd_is_active_wakeup(genpd)) return resume_noirq(dev); genpd_lock(genpd); @@ -2214,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); @@ -2229,8 +2330,10 @@ static int genpd_alloc_data(struct generic_pm_domain *genpd) return 0; put: put_device(&genpd->dev); - if (genpd->free_states == genpd_free_default_power_state) + if (genpd->free_states == genpd_free_default_power_state) { kfree(genpd->states); + genpd->states = NULL; + } free: if (genpd_is_cpu_domain(genpd)) free_cpumask_var(genpd->cpus); @@ -2289,10 +2392,13 @@ 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; genpd->has_provider = false; + genpd->opp_table = NULL; genpd->accounting_time = ktime_get_mono_fast_ns(); genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; genpd->domain.ops.runtime_resume = genpd_runtime_resume; @@ -2440,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 @@ -2506,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); @@ -2533,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. @@ -2541,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 @@ -2566,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_is_opp_table_fw(genpd) && genpd->set_performance_state) { - 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); @@ -2591,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]; @@ -2610,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; } @@ -2627,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; } @@ -2647,10 +2811,12 @@ error: genpd->provider = NULL; genpd->has_provider = false; - if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) { + if (genpd->opp_table) { dev_pm_opp_put_opp_table(genpd->opp_table); dev_pm_opp_of_remove_table(&genpd->dev); } + + device_del(&genpd->dev); } return ret; @@ -2676,18 +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 (genpd_is_opp_table_fw(gpd) || !gpd->set_performance_state) - continue; + if (gpd->opp_table) { + dev_pm_opp_put_opp_table(gpd->opp_table); + dev_pm_opp_of_remove_table(&gpd->dev); + } - 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); @@ -2866,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; @@ -3129,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) @@ -3219,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; } @@ -3305,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); @@ -3492,7 +3756,7 @@ static int idle_states_show(struct seq_file *s, void *data) if (ret) return -ERESTARTSYS; - seq_puts(s, "State Time Spent(ms) Usage Rejected\n"); + seq_puts(s, "State Time Spent(ms) Usage Rejected Above Below\n"); for (i = 0; i < genpd->state_count; i++) { struct genpd_power_state *state = &genpd->states[i]; @@ -3512,9 +3776,10 @@ static int idle_states_show(struct seq_file *s, void *data) snprintf(state_name, ARRAY_SIZE(state_name), "S%-13d", i); do_div(idle_time, NSEC_PER_MSEC); - seq_printf(s, "%-14s %-14llu %-14llu %llu\n", + seq_printf(s, "%-14s %-14llu %-10llu %-10llu %-10llu %llu\n", state->name ?: state_name, idle_time, - state->usage, state->rejected); + state->usage, state->rejected, state->above, + state->below); } genpd_unlock(genpd); diff --git a/drivers/pmdomain/governor.c b/drivers/pmdomain/governor.c index d1a10eeebd16..39359811a930 100644 --- a/drivers/pmdomain/governor.c +++ b/drivers/pmdomain/governor.c @@ -8,6 +8,7 @@ #include <linux/pm_domain.h> #include <linux/pm_qos.h> #include <linux/hrtimer.h> +#include <linux/cpu.h> #include <linux/cpuidle.h> #include <linux/cpumask.h> #include <linux/ktime.h> @@ -349,6 +350,8 @@ static bool cpu_power_down_ok(struct dev_pm_domain *pd) struct cpuidle_device *dev; ktime_t domain_wakeup, next_hrtimer; ktime_t now = ktime_get(); + struct device *cpu_dev; + s64 cpu_constraint, global_constraint; s64 idle_duration_ns; int cpu, i; @@ -359,6 +362,7 @@ static bool cpu_power_down_ok(struct dev_pm_domain *pd) if (!(genpd->flags & GENPD_FLAG_CPU_DOMAIN)) return true; + global_constraint = cpu_latency_qos_limit(); /* * Find the next wakeup for any of the online CPUs within the PM domain * and its subdomains. Note, we only need the genpd->cpus, as it already @@ -372,8 +376,16 @@ static bool cpu_power_down_ok(struct dev_pm_domain *pd) if (ktime_before(next_hrtimer, domain_wakeup)) domain_wakeup = next_hrtimer; } + + cpu_dev = get_cpu_device(cpu); + if (cpu_dev) { + cpu_constraint = dev_pm_qos_raw_resume_latency(cpu_dev); + if (cpu_constraint < global_constraint) + global_constraint = cpu_constraint; + } } + global_constraint *= NSEC_PER_USEC; /* The minimum idle duration is from now - until the next wakeup. */ idle_duration_ns = ktime_to_ns(ktime_sub(domain_wakeup, now)); if (idle_duration_ns <= 0) @@ -389,9 +401,13 @@ static bool cpu_power_down_ok(struct dev_pm_domain *pd) */ i = genpd->state_idx; do { - if (idle_duration_ns >= (genpd->states[i].residency_ns + - genpd->states[i].power_off_latency_ns)) { + if ((idle_duration_ns >= (genpd->states[i].residency_ns + + genpd->states[i].power_off_latency_ns)) && + (global_constraint >= (genpd->states[i].power_on_latency_ns + + genpd->states[i].power_off_latency_ns))) { genpd->state_idx = i; + genpd->gd->last_enter = now; + genpd->gd->reflect_residency = true; return true; } } while (--i >= 0); 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/mediatek/mt6893-pm-domains.h b/drivers/pmdomain/mediatek/mt6893-pm-domains.h new file mode 100644 index 000000000000..c9e2aa511448 --- /dev/null +++ b/drivers/pmdomain/mediatek/mt6893-pm-domains.h @@ -0,0 +1,585 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2025 Collabora Ltd + * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> + */ + +#ifndef __PMDOMAIN_MEDIATEK_MT6893_PM_DOMAINS_H +#define __PMDOMAIN_MEDIATEK_MT6893_PM_DOMAINS_H + +#include <linux/soc/mediatek/infracfg.h> +#include <dt-bindings/power/mediatek,mt6893-power.h> +#include "mtk-pm-domains.h" + +#define MT6893_TOP_AXI_PROT_EN_MCU_STA1 0x2e4 +#define MT6893_TOP_AXI_PROT_EN_MCU_SET 0x2c4 +#define MT6893_TOP_AXI_PROT_EN_MCU_CLR 0x2c8 +#define MT6893_TOP_AXI_PROT_EN_VDNR_1_SET 0xba4 +#define MT6893_TOP_AXI_PROT_EN_VDNR_1_CLR 0xba8 +#define MT6893_TOP_AXI_PROT_EN_VDNR_1_STA1 0xbb0 +#define MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET 0xbb8 +#define MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR 0xbbc +#define MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1 0xbc4 + +#define MT6893_TOP_AXI_PROT_EN_1_MFG1_STEP1 GENMASK(21, 19) +#define MT6893_TOP_AXI_PROT_EN_2_MFG1_STEP2 GENMASK(6, 5) +#define MT6893_TOP_AXI_PROT_EN_MFG1_STEP3 GENMASK(22, 21) +#define MT6893_TOP_AXI_PROT_EN_2_MFG1_STEP4 BIT(7) +#define MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_MFG1_STEP5 GENMASK(19, 17) +#define MT6893_TOP_AXI_PROT_EN_MM_VDEC0_STEP1 BIT(24) +#define MT6893_TOP_AXI_PROT_EN_MM_2_VDEC0_STEP2 BIT(25) +#define MT6893_TOP_AXI_PROT_EN_MM_VDEC1_STEP1 BIT(6) +#define MT6893_TOP_AXI_PROT_EN_MM_VDEC1_STEP2 BIT(7) +#define MT6893_TOP_AXI_PROT_EN_MM_VENC0_STEP1 BIT(26) +#define MT6893_TOP_AXI_PROT_EN_MM_2_VENC0_STEP2 BIT(0) +#define MT6893_TOP_AXI_PROT_EN_MM_VENC0_STEP3 BIT(27) +#define MT6893_TOP_AXI_PROT_EN_MM_2_VENC0_STEP4 BIT(1) +#define MT6893_TOP_AXI_PROT_EN_MM_VENC1_STEP1 GENMASK(30, 28) +#define MT6893_TOP_AXI_PROT_EN_MM_VENC1_STEP2 GENMASK(31, 29) +#define MT6893_TOP_AXI_PROT_EN_MDP_STEP1 BIT(10) +#define MT6893_TOP_AXI_PROT_EN_MM_MDP_STEP2 (BIT(2) | BIT(4) | BIT(6) | \ + BIT(8) | BIT(18) | BIT(22) | \ + BIT(28) | BIT(30)) +#define MT6893_TOP_AXI_PROT_EN_MM_2_MDP_STEP3 (BIT(0) | BIT(2) | BIT(4) | \ + BIT(6) | BIT(8)) +#define MT6893_TOP_AXI_PROT_EN_MDP_STEP4 BIT(23) +#define MT6893_TOP_AXI_PROT_EN_MM_MDP_STEP5 (BIT(3) | BIT(5) | BIT(7) | \ + BIT(9) | BIT(19) | BIT(23) | \ + BIT(29) | BIT(31)) +#define MT6893_TOP_AXI_PROT_EN_MM_2_MDP_STEP6 (BIT(1) | BIT(7) | BIT(9) | BIT(11)) +#define MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_MDP_STEP7 BIT(20) +#define MT6893_TOP_AXI_PROT_EN_MM_DISP_STEP1 (BIT(0) | BIT(6) | BIT(8) | \ + BIT(10) | BIT(12) | BIT(14) | \ + BIT(16) | BIT(20) | BIT(24) | \ + BIT(26)) +#define MT6893_TOP_AXI_PROT_EN_DISP_STEP2 BIT(6) +#define MT6893_TOP_AXI_PROT_EN_MM_DISP_STEP3 (BIT(1) | BIT(7) | BIT(9) | \ + BIT(15) | BIT(17) | BIT(21) | \ + BIT(25) | BIT(27)) +#define MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_DISP_STEP4 BIT(21) +#define MT6893_TOP_AXI_PROT_EN_2_ADSP BIT(3) +#define MT6893_TOP_AXI_PROT_EN_2_CAM_STEP1 BIT(1) +#define MT6893_TOP_AXI_PROT_EN_MM_CAM_STEP2 (BIT(0) | BIT(2) | BIT(4)) +#define MT6893_TOP_AXI_PROT_EN_1_CAM_STEP3 BIT(22) +#define MT6893_TOP_AXI_PROT_EN_MM_CAM_STEP4 (BIT(1) | BIT(3) | BIT(5)) +#define MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWA_STEP1 BIT(18) +#define MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWA_STEP2 BIT(19) +#define MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWB_STEP1 BIT(20) +#define MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWB_STEP2 BIT(21) +#define MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWC_STEP1 BIT(22) +#define MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWC_STEP2 BIT(23) + +/* + * MT6893 Power Domain (MTCMOS) support + * + * The register layout for this IP is very similar to MT8192 so where possible + * the same definitions are reused to avoid duplication. + * Where the bus protection bits are also the same, the entire set is reused. + */ +static const struct scpsys_domain_data scpsys_domain_data_mt6893[] = { + [MT6893_POWER_DOMAIN_CONN] = { + .name = "conn", + .sta_mask = BIT(1), + .ctl_offs = 0x304, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = 0, + .sram_pdn_ack_bits = 0, + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT8192_TOP_AXI_PROT_EN_CONN, + MT8192_TOP_AXI_PROT_EN_SET, + MT8192_TOP_AXI_PROT_EN_CLR, + MT8192_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(INFRA, + MT8192_TOP_AXI_PROT_EN_CONN_2ND, + MT8192_TOP_AXI_PROT_EN_SET, + MT8192_TOP_AXI_PROT_EN_CLR, + MT8192_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(INFRA, + MT8192_TOP_AXI_PROT_EN_1_CONN, + MT8192_TOP_AXI_PROT_EN_1_SET, + MT8192_TOP_AXI_PROT_EN_1_CLR, + MT8192_TOP_AXI_PROT_EN_1_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6893_POWER_DOMAIN_MFG0] = { + .name = "mfg0", + .sta_mask = BIT(2), + .ctl_offs = 0x308, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT6893_POWER_DOMAIN_MFG1] = { + .name = "mfg1", + .sta_mask = BIT(3), + .ctl_offs = 0x30c, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_1_MFG1_STEP1, + MT8192_TOP_AXI_PROT_EN_1_SET, + MT8192_TOP_AXI_PROT_EN_1_CLR, + MT8192_TOP_AXI_PROT_EN_1_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_2_MFG1_STEP2, + MT8192_TOP_AXI_PROT_EN_2_SET, + MT8192_TOP_AXI_PROT_EN_2_CLR, + MT8192_TOP_AXI_PROT_EN_2_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MFG1_STEP3, + MT8192_TOP_AXI_PROT_EN_SET, + MT8192_TOP_AXI_PROT_EN_CLR, + MT8192_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_2_MFG1_STEP4, + MT8192_TOP_AXI_PROT_EN_2_SET, + MT8192_TOP_AXI_PROT_EN_2_CLR, + MT8192_TOP_AXI_PROT_EN_2_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_MFG1_STEP5, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT6893_POWER_DOMAIN_MFG2] = { + .name = "mfg2", + .sta_mask = BIT(4), + .ctl_offs = 0x310, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6893_POWER_DOMAIN_MFG3] = { + .name = "mfg3", + .sta_mask = BIT(5), + .ctl_offs = 0x314, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6893_POWER_DOMAIN_MFG4] = { + .name = "mfg4", + .sta_mask = BIT(6), + .ctl_offs = 0x318, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6893_POWER_DOMAIN_MFG5] = { + .name = "mfg5", + .sta_mask = BIT(7), + .ctl_offs = 0x31c, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6893_POWER_DOMAIN_MFG6] = { + .name = "mfg6", + .sta_mask = BIT(8), + .ctl_offs = 0x320, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6893_POWER_DOMAIN_ISP] = { + .name = "isp", + .sta_mask = BIT(12), + .ctl_offs = 0x330, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT8192_TOP_AXI_PROT_EN_MM_2_ISP, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(INFRA, + MT8192_TOP_AXI_PROT_EN_MM_2_ISP_2ND, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + }, + }, + [MT6893_POWER_DOMAIN_ISP2] = { + .name = "isp2", + .sta_mask = BIT(13), + .ctl_offs = 0x334, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT8192_TOP_AXI_PROT_EN_MM_ISP2, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT8192_TOP_AXI_PROT_EN_MM_ISP2_2ND, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + }, + [MT6893_POWER_DOMAIN_IPE] = { + .name = "ipe", + .sta_mask = BIT(14), + .ctl_offs = 0x338, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT8192_TOP_AXI_PROT_EN_MM_IPE, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT8192_TOP_AXI_PROT_EN_MM_IPE_2ND, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + }, + [MT6893_POWER_DOMAIN_VDEC0] = { + .name = "vdec0", + .sta_mask = BIT(15), + .ctl_offs = 0x33c, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_VDEC0_STEP1, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_2_VDEC0_STEP2, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6893_POWER_DOMAIN_VDEC1] = { + .name = "vdec1", + .sta_mask = BIT(16), + .ctl_offs = 0x340, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_VDEC1_STEP1, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_VDEC1_STEP2, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6893_POWER_DOMAIN_VENC0] = { + .name = "venc0", + .sta_mask = BIT(17), + .ctl_offs = 0x344, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_VENC0_STEP1, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_2_VENC0_STEP2, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_VENC0_STEP3, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_2_VENC0_STEP4, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6893_POWER_DOMAIN_VENC1] = { + .name = "venc1", + .sta_mask = BIT(18), + .ctl_offs = 0x348, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_VENC1_STEP1, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_VENC1_STEP2, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT6893_POWER_DOMAIN_MDP] = { + .name = "mdp", + .sta_mask = BIT(19), + .ctl_offs = 0x34c, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MDP_STEP1, + MT8192_TOP_AXI_PROT_EN_SET, + MT8192_TOP_AXI_PROT_EN_CLR, + MT8192_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_MDP_STEP2, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_2_MDP_STEP3, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MDP_STEP4, + MT8192_TOP_AXI_PROT_EN_SET, + MT8192_TOP_AXI_PROT_EN_CLR, + MT8192_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_MDP_STEP5, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_2_MDP_STEP6, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_MDP_STEP7, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1), + }, + }, + [MT6893_POWER_DOMAIN_DISP] = { + .name = "disp", + .sta_mask = BIT(20), + .ctl_offs = 0x350, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_DISP_STEP1, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_DISP_STEP2, + MT8192_TOP_AXI_PROT_EN_SET, + MT8192_TOP_AXI_PROT_EN_CLR, + MT8192_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_DISP_STEP3, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_DISP_STEP4, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, + MT6893_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1), + }, + }, + [MT6893_POWER_DOMAIN_AUDIO] = { + .name = "audio", + .sta_mask = BIT(21), + .ctl_offs = 0x354, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT8192_TOP_AXI_PROT_EN_2_AUDIO, + MT8192_TOP_AXI_PROT_EN_2_SET, + MT8192_TOP_AXI_PROT_EN_2_CLR, + MT8192_TOP_AXI_PROT_EN_2_STA1), + }, + }, + [MT6893_POWER_DOMAIN_ADSP] = { + .name = "audio", + .sta_mask = BIT(22), + .ctl_offs = 0x358, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(9), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_2_ADSP, + MT8192_TOP_AXI_PROT_EN_2_SET, + MT8192_TOP_AXI_PROT_EN_2_CLR, + MT8192_TOP_AXI_PROT_EN_2_STA1), + }, + }, + [MT6893_POWER_DOMAIN_CAM] = { + .name = "cam", + .sta_mask = BIT(23), + .ctl_offs = 0x35c, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_2_CAM_STEP1, + MT8192_TOP_AXI_PROT_EN_2_SET, + MT8192_TOP_AXI_PROT_EN_2_CLR, + MT8192_TOP_AXI_PROT_EN_2_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_CAM_STEP2, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_1_CAM_STEP3, + MT8192_TOP_AXI_PROT_EN_1_SET, + MT8192_TOP_AXI_PROT_EN_1_CLR, + MT8192_TOP_AXI_PROT_EN_1_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_CAM_STEP4, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + }, + [MT6893_POWER_DOMAIN_CAM_RAWA] = { + .name = "cam_rawa", + .sta_mask = BIT(24), + .ctl_offs = 0x360, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWA_STEP1, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWA_STEP2, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + }, + [MT6893_POWER_DOMAIN_CAM_RAWB] = { + .name = "cam_rawb", + .sta_mask = BIT(25), + .ctl_offs = 0x364, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWB_STEP1, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWB_STEP2, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + }, + [MT6893_POWER_DOMAIN_CAM_RAWC] = { + .name = "cam_rawc", + .sta_mask = BIT(26), + .ctl_offs = 0x368, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_cfg = { + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWC_STEP1, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(INFRA, + MT6893_TOP_AXI_PROT_EN_MM_CAM_RAWC_STEP2, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + }, + [MT6893_POWER_DOMAIN_DP_TX] = { + .name = "dp_tx", + .sta_mask = BIT(27), + .ctl_offs = 0x3ac, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, +}; + +static const struct scpsys_soc_data mt6893_scpsys_data = { + .domains_data = scpsys_domain_data_mt6893, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt6893), +}; + +#endif /* __PMDOMAIN_MEDIATEK_MT6893_PM_DOMAINS_H */ diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index b866b006af69..a58ed7e2d9a4 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -18,6 +18,7 @@ #include "mt6735-pm-domains.h" #include "mt6795-pm-domains.h" +#include "mt6893-pm-domains.h" #include "mt8167-pm-domains.h" #include "mt8173-pm-domains.h" #include "mt8183-pm-domains.h" @@ -397,20 +398,26 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no pd->infracfg = syscon_regmap_lookup_by_phandle_optional(node, "mediatek,infracfg"); if (IS_ERR(pd->infracfg)) - return ERR_CAST(pd->infracfg); + return dev_err_cast_probe(scpsys->dev, pd->infracfg, + "%pOF: failed to get infracfg regmap\n", + node); smi_node = of_parse_phandle(node, "mediatek,smi", 0); if (smi_node) { pd->smi = device_node_to_regmap(smi_node); of_node_put(smi_node); if (IS_ERR(pd->smi)) - return ERR_CAST(pd->smi); + return dev_err_cast_probe(scpsys->dev, pd->smi, + "%pOF: failed to get SMI regmap\n", + node); } if (MTK_SCPD_CAPS(pd, MTK_SCPD_HAS_INFRA_NAO)) { pd->infracfg_nao = syscon_regmap_lookup_by_phandle(node, "mediatek,infracfg-nao"); if (IS_ERR(pd->infracfg_nao)) - return ERR_CAST(pd->infracfg_nao); + return dev_err_cast_probe(scpsys->dev, pd->infracfg_nao, + "%pOF: failed to get infracfg-nao regmap\n", + node); } else { pd->infracfg_nao = NULL; } @@ -618,6 +625,10 @@ static const struct of_device_id scpsys_of_match[] = { .data = &mt6795_scpsys_data, }, { + .compatible = "mediatek,mt6893-power-controller", + .data = &mt6893_scpsys_data, + }, + { .compatible = "mediatek,mt8167-power-controller", .data = &mt8167_scpsys_data, }, diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h index 2ac96804b985..7085fa2976e9 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.h +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h @@ -44,7 +44,7 @@ #define PWR_STATUS_AUDIO BIT(24) #define PWR_STATUS_USB BIT(25) -#define SPM_MAX_BUS_PROT_DATA 6 +#define SPM_MAX_BUS_PROT_DATA 7 enum scpsys_bus_prot_flags { BUS_PROT_REG_UPDATE = BIT(1), diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index dfd0f80154e4..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, @@ -360,6 +378,21 @@ static const struct rpmhpd_desc sdx75_desc = { .num_pds = ARRAY_SIZE(sdx75_rpmhpds), }; +/* SM4450 RPMH powerdomains */ +static struct rpmhpd *sm4450_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MSS] = &mss, + [RPMHPD_MX] = &mx, +}; + +static const struct rpmhpd_desc sm4450_desc = { + .rpmhpds = sm4450_rpmhpds, + .num_pds = ARRAY_SIZE(sm4450_rpmhpds), +}; + /* SM6350 RPMH powerdomains */ static struct rpmhpd *sm6350_rpmhpds[] = { [SM6350_CX] = &cx_w_mx_parent, @@ -651,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, @@ -708,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 }, @@ -724,6 +784,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc}, { .compatible = "qcom,sdx65-rpmhpd", .data = &sdx65_desc}, { .compatible = "qcom,sdx75-rpmhpd", .data = &sdx75_desc}, + { .compatible = "qcom,sm4450-rpmhpd", .data = &sm4450_desc }, { .compatible = "qcom,sm6350-rpmhpd", .data = &sm6350_desc }, { .compatible = "qcom,sm7150-rpmhpd", .data = &sm7150_desc }, { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, @@ -1011,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 03bcf79a461f..242570c505fb 100644 --- a/drivers/pmdomain/rockchip/pm-domains.c +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -2,7 +2,7 @@ /* * Rockchip Generic power domain support. * - * Copyright (c) 2015 ROCKCHIP, Co. Ltd. + * Copyright (c) 2015 Rockchip Electronics Co., Ltd. */ #include <linux/arm-smccc.h> @@ -35,6 +35,8 @@ #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> #include <dt-bindings/power/rk3588-power.h> @@ -135,6 +137,20 @@ struct rockchip_pmu { .active_wakeup = wakeup, \ } +#define DOMAIN_M_G_SD(_name, pwr, status, req, idle, ack, g_mask, mem, wakeup, keepon) \ +{ \ + .name = _name, \ + .pwr_w_mask = (pwr) << 16, \ + .pwr_mask = (pwr), \ + .status_mask = (status), \ + .req_w_mask = (req) << 16, \ + .req_mask = (req), \ + .idle_mask = (idle), \ + .ack_mask = (ack), \ + .clk_ungate_mask = (g_mask), \ + .active_wakeup = wakeup, \ +} + #define DOMAIN_M_O_R(_name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, ack, wakeup, regulator) \ { \ .name = _name, \ @@ -201,6 +217,12 @@ 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) + #define DOMAIN_RK3568(name, pwr, req, wakeup) \ DOMAIN_M(name, pwr, pwr, req, req, req, wakeup) @@ -1197,6 +1219,26 @@ 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), + [RK3562_PD_NPU] = DOMAIN_RK3562("npu", BIT(1), BIT(2), BIT(2), 0, false), + [RK3562_PD_VDPU] = DOMAIN_RK3562("vdpu", BIT(2), BIT(6), BIT(6), 0, false), + [RK3562_PD_VEPU] = DOMAIN_RK3562("vepu", BIT(3), BIT(7), BIT(7) | BIT(3), 0, false), + [RK3562_PD_RGA] = DOMAIN_RK3562("rga", BIT(4), BIT(5), BIT(5) | BIT(4), 0, false), + [RK3562_PD_VI] = DOMAIN_RK3562("vi", BIT(5), BIT(3), BIT(3), 0, false), + [RK3562_PD_VO] = DOMAIN_RK3562("vo", BIT(6), BIT(4), BIT(4), 16, false), + [RK3562_PD_PHP] = DOMAIN_RK3562("php", BIT(7), BIT(8), BIT(8), 0, false), +}; + static const struct rockchip_domain_info rk3568_pm_domains[] = { [RK3568_PD_NPU] = DOMAIN_RK3568("npu", BIT(1), BIT(2), false), [RK3568_PD_GPU] = DOMAIN_RK3568("gpu", BIT(0), BIT(1), false), @@ -1398,6 +1440,29 @@ 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, + .req_offset = 0x110, + .idle_offset = 0x128, + .ack_offset = 0x120, + .clk_ungate_offset = 0x140, + + .num_domains = ARRAY_SIZE(rk3562_pm_domains), + .domain_info = rk3562_pm_domains, +}; + static const struct rockchip_pmu_info rk3568_pmu = { .pwr_offset = 0xa0, .status_offset = 0x98, @@ -1497,6 +1562,14 @@ 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, + }, + { .compatible = "rockchip,rk3568-power-controller", .data = (void *)&rk3568_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 17781bf8d86d..858446594c88 100644 --- a/drivers/pmdomain/sunxi/Kconfig +++ b/drivers/pmdomain/sunxi/Kconfig @@ -1,10 +1,33 @@ # 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" + depends on ARCH_SUNXI || COMPILE_TEST + depends on PM + select PM_GENERIC_DOMAINS + help + 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 ec1d7a2fb21d..e344b232fc9f 100644 --- a/drivers/pmdomain/sunxi/Makefile +++ b/drivers/pmdomain/sunxi/Makefile @@ -1,2 +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/sun50i-h6-prcm-ppu.c b/drivers/pmdomain/sunxi/sun50i-h6-prcm-ppu.c new file mode 100644 index 000000000000..d59644499dfe --- /dev/null +++ b/drivers/pmdomain/sunxi/sun50i-h6-prcm-ppu.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) Arm Ltd. 2024 + * + * Allwinner H6/H616 PRCM power domain driver. + * This covers a few registers inside the PRCM (Power Reset Clock Management) + * block that control some power rails, most prominently for the Mali GPU. + */ + +#include <linux/bitfield.h> +#include <linux/clk.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> + +/* + * The PRCM block covers multiple devices, starting with some clocks, + * then followed by the power rails. + * The clocks are covered by a different driver, so this driver's MMIO range + * starts later in the PRCM MMIO frame, not at the beginning of it. + * To keep the register offsets consistent with other PRCM documentation, + * express the registers relative to the beginning of the whole PRCM, and + * subtract the PPU offset this driver is bound to. + */ +#define PD_H6_PPU_OFFSET 0x250 +#define PD_H6_VDD_SYS_REG 0x250 +#define PD_H616_ANA_VDD_GATE BIT(4) +#define PD_H6_CPUS_VDD_GATE BIT(3) +#define PD_H6_AVCC_VDD_GATE BIT(2) +#define PD_H6_GPU_REG 0x254 +#define PD_H6_GPU_GATE BIT(0) + +struct sun50i_h6_ppu_pd { + struct generic_pm_domain genpd; + void __iomem *reg; + u32 gate_mask; + bool negated; +}; + +#define FLAG_PPU_ALWAYS_ON BIT(0) +#define FLAG_PPU_NEGATED BIT(1) + +struct sun50i_h6_ppu_desc { + const char *name; + u32 offset; + u32 mask; + unsigned int flags; +}; + +static const struct sun50i_h6_ppu_desc sun50i_h6_ppus[] = { + { "AVCC", PD_H6_VDD_SYS_REG, PD_H6_AVCC_VDD_GATE }, + { "CPUS", PD_H6_VDD_SYS_REG, PD_H6_CPUS_VDD_GATE }, + { "GPU", PD_H6_GPU_REG, PD_H6_GPU_GATE }, +}; +static const struct sun50i_h6_ppu_desc sun50i_h616_ppus[] = { + { "PLL", PD_H6_VDD_SYS_REG, PD_H6_AVCC_VDD_GATE, + FLAG_PPU_ALWAYS_ON | FLAG_PPU_NEGATED }, + { "ANA", PD_H6_VDD_SYS_REG, PD_H616_ANA_VDD_GATE, FLAG_PPU_ALWAYS_ON }, + { "GPU", PD_H6_GPU_REG, PD_H6_GPU_GATE, FLAG_PPU_NEGATED }, +}; + +struct sun50i_h6_ppu_data { + const struct sun50i_h6_ppu_desc *descs; + int nr_domains; +}; + +static const struct sun50i_h6_ppu_data sun50i_h6_ppu_data = { + .descs = sun50i_h6_ppus, + .nr_domains = ARRAY_SIZE(sun50i_h6_ppus), +}; + +static const struct sun50i_h6_ppu_data sun50i_h616_ppu_data = { + .descs = sun50i_h616_ppus, + .nr_domains = ARRAY_SIZE(sun50i_h616_ppus), +}; + +#define to_sun50i_h6_ppu_pd(_genpd) \ + container_of(_genpd, struct sun50i_h6_ppu_pd, genpd) + +static bool sun50i_h6_ppu_power_status(const struct sun50i_h6_ppu_pd *pd) +{ + bool bit = readl(pd->reg) & pd->gate_mask; + + return bit ^ pd->negated; +} + +static int sun50i_h6_ppu_pd_set_power(const struct sun50i_h6_ppu_pd *pd, + bool set_bit) +{ + u32 reg = readl(pd->reg); + + if (set_bit) + writel(reg | pd->gate_mask, pd->reg); + else + writel(reg & ~pd->gate_mask, pd->reg); + + return 0; +} + +static int sun50i_h6_ppu_pd_power_on(struct generic_pm_domain *genpd) +{ + const struct sun50i_h6_ppu_pd *pd = to_sun50i_h6_ppu_pd(genpd); + + return sun50i_h6_ppu_pd_set_power(pd, !pd->negated); +} + +static int sun50i_h6_ppu_pd_power_off(struct generic_pm_domain *genpd) +{ + const struct sun50i_h6_ppu_pd *pd = to_sun50i_h6_ppu_pd(genpd); + + return sun50i_h6_ppu_pd_set_power(pd, pd->negated); +} + +static int sun50i_h6_ppu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct genpd_onecell_data *ppu; + struct sun50i_h6_ppu_pd *pds; + const struct sun50i_h6_ppu_data *data; + void __iomem *base; + int ret, i; + + data = of_device_get_match_data(dev); + if (!data) + return -EINVAL; + + pds = devm_kcalloc(dev, data->nr_domains, sizeof(*pds), GFP_KERNEL); + if (!pds) + return -ENOMEM; + + ppu = devm_kzalloc(dev, sizeof(*ppu), GFP_KERNEL); + if (!ppu) + return -ENOMEM; + + ppu->num_domains = data->nr_domains; + ppu->domains = devm_kcalloc(dev, data->nr_domains, + sizeof(*ppu->domains), GFP_KERNEL); + if (!ppu->domains) + return -ENOMEM; + + platform_set_drvdata(pdev, ppu); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + for (i = 0; i < data->nr_domains; i++) { + struct sun50i_h6_ppu_pd *pd = &pds[i]; + const struct sun50i_h6_ppu_desc *desc = &data->descs[i]; + + pd->genpd.name = desc->name; + pd->genpd.power_off = sun50i_h6_ppu_pd_power_off; + pd->genpd.power_on = sun50i_h6_ppu_pd_power_on; + if (desc->flags & FLAG_PPU_ALWAYS_ON) + pd->genpd.flags = GENPD_FLAG_ALWAYS_ON; + pd->negated = !!(desc->flags & FLAG_PPU_NEGATED); + pd->reg = base + desc->offset - PD_H6_PPU_OFFSET; + pd->gate_mask = desc->mask; + + ret = pm_genpd_init(&pd->genpd, NULL, + !sun50i_h6_ppu_power_status(pd)); + if (ret) { + dev_warn(dev, "Failed to add %s power domain: %d\n", + desc->name, ret); + goto out_remove_pds; + } + ppu->domains[i] = &pd->genpd; + } + + ret = of_genpd_add_provider_onecell(dev->of_node, ppu); + if (!ret) + return 0; + + dev_warn(dev, "Failed to add provider: %d\n", ret); +out_remove_pds: + for (i--; i >= 0; i--) + pm_genpd_remove(&pds[i].genpd); + + return ret; +} + +static const struct of_device_id sun50i_h6_ppu_of_match[] = { + { .compatible = "allwinner,sun50i-h6-prcm-ppu", + .data = &sun50i_h6_ppu_data }, + { .compatible = "allwinner,sun50i-h616-prcm-ppu", + .data = &sun50i_h616_ppu_data }, + { } +}; +MODULE_DEVICE_TABLE(of, sun50i_h6_ppu_of_match); + +static struct platform_driver sun50i_h6_ppu_driver = { + .probe = sun50i_h6_ppu_probe, + .driver = { + .name = "sun50i-h6-prcm-ppu", + .of_match_table = sun50i_h6_ppu_of_match, + /* Power domains cannot be removed while they are in use. */ + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(sun50i_h6_ppu_driver); + +MODULE_AUTHOR("Andre Przywara <andre.przywara@arm.com>"); +MODULE_DESCRIPTION("Allwinner H6 PRCM power domain driver"); +MODULE_LICENSE("GPL"); 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/ti/omap_prm.c b/drivers/pmdomain/ti/omap_prm.c index 79d165331d8c..5142f064bf5c 100644 --- a/drivers/pmdomain/ti/omap_prm.c +++ b/drivers/pmdomain/ti/omap_prm.c @@ -18,7 +18,9 @@ #include <linux/pm_domain.h> #include <linux/reset-controller.h> #include <linux/delay.h> - +#if IS_ENABLED(CONFIG_SUSPEND) +#include <linux/suspend.h> +#endif #include <linux/platform_data/ti-prm.h> enum omap_prm_domain_mode { @@ -88,6 +90,7 @@ struct omap_reset_data { #define OMAP_PRM_HAS_RSTST BIT(1) #define OMAP_PRM_HAS_NO_CLKDM BIT(2) #define OMAP_PRM_RET_WHEN_IDLE BIT(3) +#define OMAP_PRM_ON_WHEN_STANDBY BIT(4) #define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST) @@ -404,7 +407,8 @@ static const struct omap_prm_data am3_prm_data[] = { .name = "per", .base = 0x44e00c00, .pwrstctrl = 0xc, .pwrstst = 0x8, .dmap = &omap_prm_noinact, .rstctrl = 0x0, .rstmap = am3_per_rst_map, - .flags = OMAP_PRM_HAS_RSTCTRL, .clkdm_name = "pruss_ocp" + .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_ON_WHEN_STANDBY, + .clkdm_name = "pruss_ocp", }, { .name = "wkup", .base = 0x44e00d00, 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, |