diff options
Diffstat (limited to 'drivers/pmdomain')
-rw-r--r-- | drivers/pmdomain/amlogic/meson-ee-pwrc.c | 78 | ||||
-rw-r--r-- | drivers/pmdomain/arm/Kconfig | 6 | ||||
-rw-r--r-- | drivers/pmdomain/bcm/bcm2835-power.c | 16 | ||||
-rw-r--r-- | drivers/pmdomain/core.c | 131 | ||||
-rw-r--r-- | drivers/pmdomain/governor.c | 2 | ||||
-rw-r--r-- | drivers/pmdomain/mediatek/mt6893-pm-domains.h | 585 | ||||
-rw-r--r-- | drivers/pmdomain/mediatek/mtk-pm-domains.c | 17 | ||||
-rw-r--r-- | drivers/pmdomain/mediatek/mtk-pm-domains.h | 2 | ||||
-rw-r--r-- | drivers/pmdomain/qcom/rpmhpd.c | 16 | ||||
-rw-r--r-- | drivers/pmdomain/rockchip/pm-domains.c | 48 | ||||
-rw-r--r-- | drivers/pmdomain/sunxi/Kconfig | 10 | ||||
-rw-r--r-- | drivers/pmdomain/sunxi/Makefile | 1 | ||||
-rw-r--r-- | drivers/pmdomain/sunxi/sun50i-h6-prcm-ppu.c | 208 | ||||
-rw-r--r-- | drivers/pmdomain/ti/omap_prm.c | 8 |
14 files changed, 1027 insertions, 101 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/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/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..ff5c7f2b69ce 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -304,10 +304,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 +758,31 @@ int dev_pm_genpd_rpm_always_on(struct device *dev, bool on) } EXPORT_SYMBOL_GPL(dev_pm_genpd_rpm_always_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 +908,24 @@ 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. + * The domain is already in the "power off" state. + * System suspend is in progress. + * The domain is configured as always on. + * The domain has a subdomain being powered on. */ - 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. - */ - 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) || + atomic_read(&genpd->sd_count) > 0) + return; /* * The children must be in their deepest (powered-off) states to allow @@ -888,7 +936,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 +950,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 +967,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 +984,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 +1002,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 @@ -1450,7 +1498,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 +1553,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); @@ -2229,8 +2277,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); @@ -2293,6 +2343,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd, 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; @@ -2567,7 +2618,7 @@ 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) { + if (genpd->opp_table) { dev_pm_opp_put_opp_table(genpd->opp_table); dev_pm_opp_of_remove_table(&genpd->dev); } @@ -2647,7 +2698,7 @@ 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); } @@ -2679,11 +2730,10 @@ void of_genpd_del_provider(struct device_node *np) if (gpd->provider == &np->fwnode) { gpd->has_provider = false; - if (genpd_is_opp_table_fw(gpd) || !gpd->set_performance_state) - continue; - - dev_pm_opp_put_opp_table(gpd->opp_table); - dev_pm_opp_of_remove_table(&gpd->dev); + if (gpd->opp_table) { + dev_pm_opp_put_opp_table(gpd->opp_table); + dev_pm_opp_of_remove_table(&gpd->dev); + } } } @@ -3492,7 +3542,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 +3562,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..c1e148657c87 100644 --- a/drivers/pmdomain/governor.c +++ b/drivers/pmdomain/governor.c @@ -392,6 +392,8 @@ static bool cpu_power_down_ok(struct dev_pm_domain *pd) if (idle_duration_ns >= (genpd->states[i].residency_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/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..078323b85b56 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -360,6 +360,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, @@ -724,6 +739,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 }, diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c index 03bcf79a461f..4cce407bb1eb 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,7 @@ #include <dt-bindings/power/rk3366-power.h> #include <dt-bindings/power/rk3368-power.h> #include <dt-bindings/power/rk3399-power.h> +#include <dt-bindings/power/rockchip,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 +136,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 +216,9 @@ struct rockchip_pmu { #define DOMAIN_RK3399(name, pwr, status, req, wakeup) \ DOMAIN(name, pwr, status, req, req, req, wakeup) +#define DOMAIN_RK3562(name, pwr, req, g_mask, mem, wakeup) \ + DOMAIN_M_G_SD(name, pwr, pwr, req, req, req, g_mask, mem, wakeup, false) + #define DOMAIN_RK3568(name, pwr, req, wakeup) \ DOMAIN_M(name, pwr, pwr, req, req, req, wakeup) @@ -1197,6 +1215,18 @@ 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 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 +1428,18 @@ static const struct rockchip_pmu_info rk3399_pmu = { .domain_info = rk3399_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 +1539,10 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = { .data = (void *)&rk3399_pmu, }, { + .compatible = "rockchip,rk3562-power-controller", + .data = (void *)&rk3562_pmu, + }, + { .compatible = "rockchip,rk3568-power-controller", .data = (void *)&rk3568_pmu, }, diff --git a/drivers/pmdomain/sunxi/Kconfig b/drivers/pmdomain/sunxi/Kconfig index 17781bf8d86d..43eecb3ea981 100644 --- a/drivers/pmdomain/sunxi/Kconfig +++ b/drivers/pmdomain/sunxi/Kconfig @@ -8,3 +8,13 @@ config SUN20I_PPU help Say y to enable the PPU power domain driver. This saves power when certain peripherals, such as the video engine, are idle. + +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. diff --git a/drivers/pmdomain/sunxi/Makefile b/drivers/pmdomain/sunxi/Makefile index ec1d7a2fb21d..c1343e123759 100644 --- a/drivers/pmdomain/sunxi/Makefile +++ b/drivers/pmdomain/sunxi/Makefile @@ -1,2 +1,3 @@ # 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 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/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, |