From ca824b2d69c52b3a9cc51b4b7f9ed8c8e554487a Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 30 Sep 2024 12:45:23 +0800 Subject: [PATCH 01/25] pmdomain: mediatek: Use OF-specific regulator API to get power domain supply The MediaTek power domain driver contains a hack that assigns the device node of the power domain to the struct device of the power domain controller in order to use the devres regulator API. Now that there is a proper OF-specific regulator API, and even a devres version, replace the hack with proper code. This change is incompatible with incomplete device trees. Instead of assigning the dummy regulator in cases where the power domain requires a supply but the device tree does not provide one, the driver will just error out. This will be seen on the MT8390 EVK, which is missing supplies for the IMG_VCORE and CAM_VCORE domains. And likely all the MediaTek EVBs, which have no power domain supplies specified. This is however the correct behavior. If the power domain's supply is missing, then it should not work. Relying on other parts of the system to keep the unattached regulator enabled is likely to break in ways less easier to understand. Signed-off-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20240930044525.2043884-4-wenst@chromium.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-pm-domains.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index 88406e9ac63c..3580913f25d3 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -353,7 +353,6 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no { const struct scpsys_domain_data *domain_data; struct scpsys_domain *pd; - struct device_node *root_node = scpsys->dev->of_node; struct device_node *smi_node; struct property *prop; const char *clk_name; @@ -388,16 +387,7 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no pd->scpsys = scpsys; if (MTK_SCPD_CAPS(pd, MTK_SCPD_DOMAIN_SUPPLY)) { - /* - * Find regulator in current power domain node. - * devm_regulator_get() finds regulator in a node and its child - * node, so set of_node to current power domain node then change - * back to original node after regulator is found for current - * power domain node. - */ - scpsys->dev->of_node = node; - pd->supply = devm_regulator_get(scpsys->dev, "domain"); - scpsys->dev->of_node = root_node; + pd->supply = devm_of_regulator_get_optional(scpsys->dev, node, "domain"); if (IS_ERR(pd->supply)) return dev_err_cast_probe(scpsys->dev, pd->supply, "%pOF: failed to get power supply.\n", From 4138d1aeaf37a15d5d23896f552f02b55691d40a Mon Sep 17 00:00:00 2001 From: Tingguo Cheng Date: Fri, 27 Sep 2024 19:59:13 +0800 Subject: [PATCH 02/25] dt-bindings: power: qcom,rpmpd: document qcs8300 RPMh power domains Add compatible string for the RPMh power domains on qcs8300 platform. Signed-off-by: Tingguo Cheng Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240927-add_qcs615_qcs8300_powerdomains_driver_support-v2-1-18c030ad7b68@quicinc.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/power/qcom,rpmpd.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 929b7ef9c1bc..be1a9cb71a9b 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -32,6 +32,7 @@ properties: - qcom,msm8998-rpmpd - qcom,qcm2290-rpmpd - qcom,qcs404-rpmpd + - qcom,qcs8300-rpmhpd - qcom,qdu1000-rpmhpd - qcom,qm215-rpmpd - qcom,sa8155p-rpmhpd From 6c8ea090aede45fa904d9e5e84ad012550e70b87 Mon Sep 17 00:00:00 2001 From: Tingguo Cheng Date: Fri, 27 Sep 2024 19:59:15 +0800 Subject: [PATCH 03/25] dt-bindings: power: qcom,rpmpd: document qcs615 RPMh power domains Add compatible string for the RPMh power domains on qcs615 platform. Signed-off-by: Tingguo Cheng Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240927-add_qcs615_qcs8300_powerdomains_driver_support-v2-3-18c030ad7b68@quicinc.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/power/qcom,rpmpd.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index be1a9cb71a9b..17ad46e55529 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -32,6 +32,7 @@ properties: - qcom,msm8998-rpmpd - qcom,qcm2290-rpmpd - qcom,qcs404-rpmpd + - qcom,qcs615-rpmhpd - qcom,qcs8300-rpmhpd - qcom,qdu1000-rpmhpd - qcom,qm215-rpmpd From aaccaf7aa102c837053e095405e37d411b947349 Mon Sep 17 00:00:00 2001 From: Tingguo Cheng Date: Fri, 27 Sep 2024 19:59:14 +0800 Subject: [PATCH 04/25] pmdomain: qcom: rpmhpd: Add qcs8300 power domains Add support for the power domains exposed by RPMh on the qcs8300 platform. MMCX depends on CX, so mark CX as the parent of MMCX. Co-developed-by: Shazad Hussain Signed-off-by: Shazad Hussain Signed-off-by: Tingguo Cheng Link: https://lore.kernel.org/r/20240927-add_qcs615_qcs8300_powerdomains_driver_support-v2-2-18c030ad7b68@quicinc.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmhpd.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 65505e1e2219..d256110225dc 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -624,7 +624,31 @@ static const struct rpmhpd_desc x1e80100_desc = { .num_pds = ARRAY_SIZE(x1e80100_rpmhpds), }; +/* QCS8300 RPMH power domains */ +static struct rpmhpd *qcs8300_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MMCX] = &mmcx_w_cx_parent, + [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_NSP0] = &nsp0, + [RPMHPD_NSP1] = &nsp1, +}; + +static const struct rpmhpd_desc qcs8300_desc = { + .rpmhpds = qcs8300_rpmhpds, + .num_pds = ARRAY_SIZE(qcs8300_rpmhpds), +}; + static const struct of_device_id rpmhpd_match_table[] = { + { .compatible = "qcom,qcs8300-rpmhpd", .data = &qcs8300_desc }, { .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc }, { .compatible = "qcom,sa8155p-rpmhpd", .data = &sa8155p_desc }, { .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc }, From ba84ca67acd5b9eb428a88065c288e10eab84413 Mon Sep 17 00:00:00 2001 From: Tingguo Cheng Date: Fri, 27 Sep 2024 19:59:16 +0800 Subject: [PATCH 05/25] pmdomain: qcom: rpmhpd: Add qcs615 power domains Add support for power domains exposed by RPMh on the qcs615 platform. Only CX/CX_AO are exposed by RPMh. Consumers such as VDD_MX, EBI, LPI_ MX and LPI_CX will share the power domains. Signed-off-by: Tingguo Cheng Link: https://lore.kernel.org/r/20240927-add_qcs615_qcs8300_powerdomains_driver_support-v2-4-18c030ad7b68@quicinc.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmhpd.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index d256110225dc..731feab9f17d 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -647,7 +647,19 @@ static const struct rpmhpd_desc qcs8300_desc = { .num_pds = ARRAY_SIZE(qcs8300_rpmhpds), }; +/* QCS615 RPMH powerdomains */ +static struct rpmhpd *qcs615_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, +}; + +static const struct rpmhpd_desc qcs615_desc = { + .rpmhpds = qcs615_rpmhpds, + .num_pds = ARRAY_SIZE(qcs615_rpmhpds), +}; + static const struct of_device_id rpmhpd_match_table[] = { + { .compatible = "qcom,qcs615-rpmhpd", .data = &qcs615_desc }, { .compatible = "qcom,qcs8300-rpmhpd", .data = &qcs8300_desc }, { .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc }, { .compatible = "qcom,sa8155p-rpmhpd", .data = &sa8155p_desc }, From 0e8158b4a82eb5bfb69df17510d210b073896f00 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 Oct 2024 14:22:24 +0200 Subject: [PATCH 06/25] OPP: Rework _set_required_devs() to manage a single device per call At this point there are no consumer drivers that makes use of _set_required_devs(), hence it should be straightforward to rework the code to enable it to better integrate with the PM domain attach procedure. During attach, one device at the time is being hooked up to its corresponding PM domain. Therefore, let's update the _set_required_devs() to align to this behaviour, allowing callers to fill out one required_dev per call. Subsequent changes starts making use of this. Signed-off-by: Ulf Hansson Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20241002122232.194245-4-ulf.hansson@linaro.org --- drivers/opp/core.c | 88 ++++++++++++++++++++++++++++-------------- drivers/opp/opp.h | 4 +- include/linux/pm_opp.h | 10 +++-- 3 files changed, 67 insertions(+), 35 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 3aa18737470f..40e5f29a2670 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2473,47 +2473,73 @@ err: } -static int _opp_set_required_devs(struct opp_table *opp_table, - struct device *dev, - struct device **required_devs) +static int _opp_set_required_dev(struct opp_table *opp_table, + struct device *dev, + struct device *required_dev, + unsigned int index) { - int i; + struct opp_table *required_table, *pd_table; + struct device *gdev; - if (!opp_table->required_devs) { + /* Genpd core takes care of propagation to parent genpd */ + if (opp_table->is_genpd) { + dev_err(dev, "%s: Operation not supported for genpds\n", __func__); + return -EOPNOTSUPP; + } + + if (index >= opp_table->required_opp_count) { dev_err(dev, "Required OPPs not available, can't set required devs\n"); return -EINVAL; } - /* Another device that shares the OPP table has set the required devs ? */ - if (opp_table->required_devs[0]) - return 0; - - for (i = 0; i < opp_table->required_opp_count; i++) { - /* Genpd core takes care of propagation to parent genpd */ - if (required_devs[i] && opp_table->is_genpd && - opp_table->required_opp_tables[i]->is_genpd) { - dev_err(dev, "%s: Operation not supported for genpds\n", __func__); - return -EOPNOTSUPP; - } - - opp_table->required_devs[i] = required_devs[i]; + required_table = opp_table->required_opp_tables[index]; + if (IS_ERR(required_table)) { + dev_err(dev, "Missing OPP table, unable to set the required devs\n"); + return -ENODEV; } + /* + * The required_opp_tables parsing is not perfect, as the OPP core does + * the parsing solely based on the DT node pointers. The core sets the + * required_opp_tables entry to the first OPP table in the "opp_tables" + * list, that matches with the node pointer. + * + * If the target DT OPP table is used by multiple devices and they all + * create separate instances of 'struct opp_table' from it, then it is + * possible that the required_opp_tables entry may be set to the + * incorrect sibling device. + * + * Cross check it again and fix if required. + */ + gdev = dev_to_genpd_dev(required_dev); + if (IS_ERR(gdev)) + return PTR_ERR(gdev); + + pd_table = _find_opp_table(gdev); + if (!IS_ERR(pd_table)) { + if (pd_table != required_table) { + dev_pm_opp_put_opp_table(required_table); + opp_table->required_opp_tables[index] = pd_table; + } else { + dev_pm_opp_put_opp_table(pd_table); + } + } + + opp_table->required_devs[index] = required_dev; return 0; } -static void _opp_put_required_devs(struct opp_table *opp_table) +static void _opp_put_required_dev(struct opp_table *opp_table, + unsigned int index) { - int i; - - for (i = 0; i < opp_table->required_opp_count; i++) - opp_table->required_devs[i] = NULL; + opp_table->required_devs[index] = NULL; } static void _opp_clear_config(struct opp_config_data *data) { - if (data->flags & OPP_CONFIG_REQUIRED_DEVS) - _opp_put_required_devs(data->opp_table); + if (data->flags & OPP_CONFIG_REQUIRED_DEV) + _opp_put_required_dev(data->opp_table, + data->required_dev_index); else if (data->flags & OPP_CONFIG_GENPD) _opp_detach_genpd(data->opp_table); @@ -2630,7 +2656,7 @@ int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config) /* Attach genpds */ if (config->genpd_names) { - if (config->required_devs) { + if (config->required_dev) { ret = -EINVAL; goto err; } @@ -2641,13 +2667,15 @@ int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config) goto err; data->flags |= OPP_CONFIG_GENPD; - } else if (config->required_devs) { - ret = _opp_set_required_devs(opp_table, dev, - config->required_devs); + } else if (config->required_dev) { + ret = _opp_set_required_dev(opp_table, dev, + config->required_dev, + config->required_dev_index); if (ret) goto err; - data->flags |= OPP_CONFIG_REQUIRED_DEVS; + data->required_dev_index = config->required_dev_index; + data->flags |= OPP_CONFIG_REQUIRED_DEV; } ret = xa_alloc(&opp_configs, &id, data, XA_LIMIT(1, INT_MAX), diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index cff1fabd1ae3..3eed6f145e90 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -35,12 +35,13 @@ extern struct list_head opp_tables; #define OPP_CONFIG_PROP_NAME BIT(3) #define OPP_CONFIG_SUPPORTED_HW BIT(4) #define OPP_CONFIG_GENPD BIT(5) -#define OPP_CONFIG_REQUIRED_DEVS BIT(6) +#define OPP_CONFIG_REQUIRED_DEV BIT(6) /** * struct opp_config_data - data for set config operations * @opp_table: OPP table * @flags: OPP config flags + * @required_dev_index: The position in the array of required_devs * * This structure stores the OPP config information for each OPP table * configuration by the callers. @@ -48,6 +49,7 @@ extern struct list_head opp_tables; struct opp_config_data { struct opp_table *opp_table; unsigned int flags; + unsigned int required_dev_index; }; /** diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 6424692c30b7..bc74bc69107a 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -63,10 +63,11 @@ typedef int (*config_clks_t)(struct device *dev, struct opp_table *opp_table, * @supported_hw_count: Number of elements in the array. * @regulator_names: Array of pointers to the names of the regulator, NULL terminated. * @genpd_names: Null terminated array of pointers containing names of genpd to - * attach. Mutually exclusive with required_devs. + * attach. Mutually exclusive with required_dev. * @virt_devs: Pointer to return the array of genpd virtual devices. Mutually - * exclusive with required_devs. - * @required_devs: Required OPP devices. Mutually exclusive with genpd_names/virt_devs. + * exclusive with required_dev. + * @required_dev: Required OPP device. Mutually exclusive with genpd_names/virt_devs. + * @required_dev_index: The index of the required OPP for the @required_dev. * * This structure contains platform specific OPP configurations for the device. */ @@ -81,7 +82,8 @@ struct dev_pm_opp_config { const char * const *regulator_names; const char * const *genpd_names; struct device ***virt_devs; - struct device **required_devs; + struct device *required_dev; + unsigned int required_dev_index; }; #define OPP_LEVEL_UNSET U32_MAX From 98d277a79126a2df8a2617215846d01f823cfffa Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 Oct 2024 14:22:25 +0200 Subject: [PATCH 07/25] PM: domains: Support required OPPs in dev_pm_domain_attach_list() In the multiple PM domain case we need platform code to specify the index of the corresponding required OPP in DT for a device, which is what *_opp_attach_genpd() is there to help us with. However, attaching a device to its PM domains is in general better done with dev_pm_domain_attach_list(). To avoid having two different ways to manage this and to prepare for the removal of *_opp_attach_genpd(), let's extend dev_pm_domain_attach|detach_list() to manage the required OPPs too. Signed-off-by: Ulf Hansson Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20241002122232.194245-5-ulf.hansson@linaro.org --- drivers/base/power/common.c | 21 ++++++++++++++++++++- include/linux/pm_domain.h | 8 ++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index cca2fd0a1aed..781968a128ff 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "power.h" @@ -222,13 +223,15 @@ int dev_pm_domain_attach_list(struct device *dev, if (!pds) return -ENOMEM; - size = sizeof(*pds->pd_devs) + sizeof(*pds->pd_links); + size = sizeof(*pds->pd_devs) + sizeof(*pds->pd_links) + + sizeof(*pds->opp_tokens); pds->pd_devs = kcalloc(num_pds, size, GFP_KERNEL); if (!pds->pd_devs) { ret = -ENOMEM; goto free_pds; } pds->pd_links = (void *)(pds->pd_devs + num_pds); + pds->opp_tokens = (void *)(pds->pd_links + num_pds); if (link_flags && pd_flags & PD_FLAG_DEV_LINK_ON) link_flags |= DL_FLAG_RPM_ACTIVE; @@ -244,6 +247,19 @@ int dev_pm_domain_attach_list(struct device *dev, goto err_attach; } + if (pd_flags & PD_FLAG_REQUIRED_OPP) { + struct dev_pm_opp_config config = { + .required_dev = pd_dev, + .required_dev_index = i, + }; + + ret = dev_pm_opp_set_config(dev, &config); + if (ret < 0) + goto err_link; + + pds->opp_tokens[i] = ret; + } + if (link_flags) { struct device_link *link; @@ -264,9 +280,11 @@ int dev_pm_domain_attach_list(struct device *dev, return num_pds; err_link: + dev_pm_opp_clear_config(pds->opp_tokens[i]); dev_pm_domain_detach(pd_dev, true); err_attach: while (--i >= 0) { + dev_pm_opp_clear_config(pds->opp_tokens[i]); if (pds->pd_links[i]) device_link_del(pds->pd_links[i]); dev_pm_domain_detach(pds->pd_devs[i], true); @@ -361,6 +379,7 @@ void dev_pm_domain_detach_list(struct dev_pm_domain_list *list) return; for (i = 0; i < list->num_pds; i++) { + dev_pm_opp_clear_config(list->opp_tokens[i]); if (list->pd_links[i]) device_link_del(list->pd_links[i]); dev_pm_domain_detach(list->pd_devs[i], true); diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index b637ec14025f..92f9d56f623d 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -30,9 +30,16 @@ * supplier and its PM domain when creating the * device-links. * + * PD_FLAG_REQUIRED_OPP: Assign required_devs for the required OPPs. The + * index of the required OPP must correspond to the + * index in the array of the pd_names. If pd_names + * isn't specified, the index just follows the + * index for the attached PM domain. + * */ #define PD_FLAG_NO_DEV_LINK BIT(0) #define PD_FLAG_DEV_LINK_ON BIT(1) +#define PD_FLAG_REQUIRED_OPP BIT(2) struct dev_pm_domain_attach_data { const char * const *pd_names; @@ -43,6 +50,7 @@ struct dev_pm_domain_attach_data { struct dev_pm_domain_list { struct device **pd_devs; struct device_link **pd_links; + u32 *opp_tokens; u32 num_pds; }; From cf06fb6f0b07012cd6cf863ecb93a57773e4e16e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 Oct 2024 14:22:26 +0200 Subject: [PATCH 08/25] pmdomain: core: Manage the default required OPP from a separate function To improve the readability of the code in __genpd_dev_pm_attach(), let's move out the required OPP handling into a separate function. Acked-by: Viresh Kumar Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20241002122232.194245-6-ulf.hansson@linaro.org --- drivers/pmdomain/core.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 5ede0f7eda09..259abd338f4c 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -2884,12 +2884,34 @@ static void genpd_dev_pm_sync(struct device *dev) genpd_queue_power_off_work(pd); } +static int genpd_set_required_opp(struct device *dev, unsigned int index) +{ + int ret, pstate; + + /* Set the default performance state */ + pstate = of_get_required_opp_performance_state(dev->of_node, index); + if (pstate < 0 && pstate != -ENODEV && pstate != -EOPNOTSUPP) { + ret = pstate; + goto err; + } else if (pstate > 0) { + ret = dev_pm_genpd_set_performance_state(dev, pstate); + if (ret) + goto err; + dev_gpd_data(dev)->default_pstate = pstate; + } + + return 0; +err: + dev_err(dev, "failed to set required performance state for power-domain %s: %d\n", + dev_to_genpd(dev)->name, ret); + return ret; +} + static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, unsigned int index, bool power_on) { struct of_phandle_args pd_args; struct generic_pm_domain *pd; - int pstate; int ret; ret = of_parse_phandle_with_args(dev->of_node, "power-domains", @@ -2918,17 +2940,9 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, dev->pm_domain->detach = genpd_dev_pm_detach; dev->pm_domain->sync = genpd_dev_pm_sync; - /* Set the default performance state */ - pstate = of_get_required_opp_performance_state(dev->of_node, index); - if (pstate < 0 && pstate != -ENODEV && pstate != -EOPNOTSUPP) { - ret = pstate; + ret = genpd_set_required_opp(dev, index); + if (ret) goto err; - } else if (pstate > 0) { - ret = dev_pm_genpd_set_performance_state(dev, pstate); - if (ret) - goto err; - dev_gpd_data(dev)->default_pstate = pstate; - } if (power_on) { genpd_lock(pd); @@ -2950,8 +2964,6 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, return 1; err: - dev_err(dev, "failed to set required performance state for power-domain %s: %d\n", - pd->name, ret); genpd_remove_device(pd, dev); return ret; } From e130ca9d4873f8d01e3e7b8137e0c14196a3cfb4 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 Oct 2024 14:22:27 +0200 Subject: [PATCH 09/25] pmdomain: core: Set the required dev for a required OPP during genpd attach In the single PM domain case there is no need for platform code to specify the index of the corresponding required OPP in DT, as the index must be zero. This allows us to assign a required dev for the required OPP from genpd, while attaching a device to its PM domain. In this way, we can remove some of the genpd specific code in the OPP core for the single PM domain case. Although, this cleanup is made from a subsequent change. Signed-off-by: Ulf Hansson Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20241002122232.194245-7-ulf.hansson@linaro.org --- drivers/pmdomain/core.c | 42 ++++++++++++++++++++++++++++++++++++--- include/linux/pm_domain.h | 1 + 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 259abd338f4c..76490f0bf1e2 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -1722,6 +1722,7 @@ static void genpd_free_dev_data(struct device *dev, spin_unlock_irq(&dev->power.lock); + dev_pm_opp_clear_config(gpd_data->opp_token); kfree(gpd_data->td); kfree(gpd_data); dev_pm_put_subsys_data(dev); @@ -2884,6 +2885,29 @@ static void genpd_dev_pm_sync(struct device *dev) genpd_queue_power_off_work(pd); } +static int genpd_set_required_opp_dev(struct device *dev, + struct device *base_dev) +{ + struct dev_pm_opp_config config = { + .required_dev = dev, + }; + int ret; + + /* Limit support to non-providers for now. */ + if (of_property_present(base_dev->of_node, "#power-domain-cells")) + return 0; + + if (!dev_pm_opp_of_has_required_opp(base_dev)) + return 0; + + ret = dev_pm_opp_set_config(base_dev, &config); + if (ret < 0) + return ret; + + dev_gpd_data(dev)->opp_token = ret; + return 0; +} + static int genpd_set_required_opp(struct device *dev, unsigned int index) { int ret, pstate; @@ -2908,7 +2932,8 @@ err: } static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, - unsigned int index, bool power_on) + unsigned int index, unsigned int num_domains, + bool power_on) { struct of_phandle_args pd_args; struct generic_pm_domain *pd; @@ -2940,6 +2965,17 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, dev->pm_domain->detach = genpd_dev_pm_detach; dev->pm_domain->sync = genpd_dev_pm_sync; + /* + * For a single PM domain the index of the required OPP must be zero, so + * let's try to assign a required dev in that case. In the multiple PM + * domains case, we need platform code to specify the index. + */ + if (num_domains == 1) { + ret = genpd_set_required_opp_dev(dev, base_dev); + if (ret) + goto err; + } + ret = genpd_set_required_opp(dev, index); if (ret) goto err; @@ -2994,7 +3030,7 @@ int genpd_dev_pm_attach(struct device *dev) "#power-domain-cells") != 1) return 0; - return __genpd_dev_pm_attach(dev, dev, 0, true); + return __genpd_dev_pm_attach(dev, dev, 0, 1, true); } EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); @@ -3047,7 +3083,7 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev, } /* Try to attach the device to the PM domain at the specified index. */ - ret = __genpd_dev_pm_attach(virt_dev, dev, index, false); + ret = __genpd_dev_pm_attach(virt_dev, dev, index, num_domains, false); if (ret < 1) { device_unregister(virt_dev); return ret ? ERR_PTR(ret) : NULL; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 92f9d56f623d..76775ab38898 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -252,6 +252,7 @@ struct generic_pm_domain_data { unsigned int performance_state; unsigned int default_pstate; unsigned int rpm_pstate; + unsigned int opp_token; bool hw_mode; void *data; }; From 0880d087c98aa028fb94bdc9b724fdff0f5916b5 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 Oct 2024 14:22:28 +0200 Subject: [PATCH 10/25] OPP: Drop redundant code in _link_required_opps() Due to that the required-devs for the required OPPs are now always being assigned, we no longer need the special treatment in _link_required_opps() for the single PM domain case. Let's therefore drop it. Acked-by: Viresh Kumar Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20241002122232.194245-8-ulf.hansson@linaro.org --- drivers/opp/of.c | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 55c8cfef97d4..fd5ed2858258 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -295,7 +295,7 @@ void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp) of_node_put(opp->np); } -static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *opp_table, +static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *required_table, int index) { struct device_node *np; @@ -313,39 +313,6 @@ static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *opp_tab return -ENODEV; } - /* - * There are two genpd (as required-opp) cases that we need to handle, - * devices with a single genpd and ones with multiple genpds. - * - * The single genpd case requires special handling as we need to use the - * same `dev` structure (instead of a virtual one provided by genpd - * core) for setting the performance state. - * - * It doesn't make sense for a device's DT entry to have both - * "opp-level" and single "required-opps" entry pointing to a genpd's - * OPP, as that would make the OPP core call - * dev_pm_domain_set_performance_state() for two different values for - * the same device structure. Lets treat single genpd configuration as a - * case where the OPP's level is directly available without required-opp - * link in the DT. - * - * Just update the `level` with the right value, which - * dev_pm_opp_set_opp() will take care of in the normal path itself. - * - * There is another case though, where a genpd's OPP table has - * required-opps set to a parent genpd. The OPP core expects the user to - * set the respective required `struct device` pointer via - * dev_pm_opp_set_config(). - */ - if (required_table->is_genpd && opp_table->required_opp_count == 1 && - !opp_table->required_devs[0]) { - /* Genpd core takes care of propagation to parent genpd */ - if (!opp_table->is_genpd) { - if (!WARN_ON(opp->level != OPP_LEVEL_UNSET)) - opp->level = opp->required_opps[0]->level; - } - } - return 0; } @@ -370,7 +337,7 @@ static int _of_opp_alloc_required_opps(struct opp_table *opp_table, if (IS_ERR_OR_NULL(required_table)) continue; - ret = _link_required_opps(opp, opp_table, required_table, i); + ret = _link_required_opps(opp, required_table, i); if (ret) goto free_required_opps; } @@ -391,7 +358,7 @@ static int lazy_link_required_opps(struct opp_table *opp_table, int ret; list_for_each_entry(opp, &opp_table->opp_list, node) { - ret = _link_required_opps(opp, opp_table, new_table, index); + ret = _link_required_opps(opp, new_table, index); if (ret) return ret; } From 1ce91bf7daa3507d181412de9efcb9348eeeab53 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 Oct 2024 14:22:29 +0200 Subject: [PATCH 11/25] drm/tegra: gr3d: Convert into devm_pm_domain_attach_list() Rather than hooking up the PM domains through devm_pm_opp_attach_genpd() and manage the device-link, let's avoid the boilerplate-code by converting into devm_pm_domain_attach_list(). Acked-by: Viresh Kumar Acked-by: Thierry Reding Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20241002122232.194245-9-ulf.hansson@linaro.org --- drivers/gpu/drm/tegra/gr3d.c | 39 ++++++++---------------------------- 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 00c8564520e7..caee824832b3 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -46,6 +46,7 @@ struct gr3d { unsigned int nclocks; struct reset_control_bulk_data resets[RST_GR3D_MAX]; unsigned int nresets; + struct dev_pm_domain_list *pd_list; DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS); }; @@ -369,18 +370,13 @@ static int gr3d_power_up_legacy_domain(struct device *dev, const char *name, return 0; } -static void gr3d_del_link(void *link) -{ - device_link_del(link); -} - static int gr3d_init_power(struct device *dev, struct gr3d *gr3d) { - static const char * const opp_genpd_names[] = { "3d0", "3d1", NULL }; - const u32 link_flags = DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME; - struct device **opp_virt_devs, *pd_dev; - struct device_link *link; - unsigned int i; + struct dev_pm_domain_attach_data pd_data = { + .pd_names = (const char *[]) { "3d0", "3d1" }, + .num_pd_names = 2, + .pd_flags = PD_FLAG_REQUIRED_OPP, + }; int err; err = of_count_phandle_with_args(dev->of_node, "power-domains", @@ -414,29 +410,10 @@ static int gr3d_init_power(struct device *dev, struct gr3d *gr3d) if (dev->pm_domain) return 0; - err = devm_pm_opp_attach_genpd(dev, opp_genpd_names, &opp_virt_devs); - if (err) + err = devm_pm_domain_attach_list(dev, &pd_data, &gr3d->pd_list); + if (err < 0) return err; - for (i = 0; opp_genpd_names[i]; i++) { - pd_dev = opp_virt_devs[i]; - if (!pd_dev) { - dev_err(dev, "failed to get %s power domain\n", - opp_genpd_names[i]); - return -EINVAL; - } - - link = device_link_add(dev, pd_dev, link_flags); - if (!link) { - dev_err(dev, "failed to link to %s\n", dev_name(pd_dev)); - return -EINVAL; - } - - err = devm_add_action_or_reset(dev, gr3d_del_link, link); - if (err) - return err; - } - return 0; } From a79d5da21b40ff524d353693e9c5dae8e46311dd Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 Oct 2024 14:22:30 +0200 Subject: [PATCH 12/25] media: venus: Convert into devm_pm_domain_attach_list() for OPP PM domain Rather than hooking up the PM domain through devm_pm_opp_attach_genpd() and manage the device-link, let's avoid the boilerplate-code by converting into devm_pm_domain_attach_list(). Acked-by: Stanimir Varbanov Acked-by: Viresh Kumar Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20241002122232.194245-10-ulf.hansson@linaro.org --- drivers/media/platform/qcom/venus/core.c | 8 ++-- drivers/media/platform/qcom/venus/core.h | 6 +-- .../media/platform/qcom/venus/pm_helpers.c | 44 +++++-------------- 3 files changed, 15 insertions(+), 43 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 84e95a46dfc9..8ad36ed0ca8b 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -752,7 +752,7 @@ static const struct venus_resources sdm845_res_v2 = { .vcodec_clks_num = 2, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, .vcodec_pmdomains_num = 3, - .opp_pmdomain = (const char *[]) { "cx", NULL }, + .opp_pmdomain = (const char *[]) { "cx" }, .vcodec_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, @@ -801,7 +801,7 @@ static const struct venus_resources sc7180_res = { .vcodec_clks_num = 2, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, - .opp_pmdomain = (const char *[]) { "cx", NULL }, + .opp_pmdomain = (const char *[]) { "cx" }, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50, @@ -858,7 +858,7 @@ static const struct venus_resources sm8250_res = { .vcodec_clks_num = 1, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, - .opp_pmdomain = (const char *[]) { "mx", NULL }, + .opp_pmdomain = (const char *[]) { "mx" }, .vcodec_num = 1, .max_load = 7833600, .hfi_version = HFI_VERSION_6XX, @@ -917,7 +917,7 @@ static const struct venus_resources sc7280_res = { .vcodec_clks_num = 2, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, - .opp_pmdomain = (const char *[]) { "cx", NULL }, + .opp_pmdomain = (const char *[]) { "cx" }, .vcodec_num = 1, .hfi_version = HFI_VERSION_6XX, .vpu_version = VPU_VERSION_IRIS2_1, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 55202b89e1b9..435325432922 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -132,9 +132,7 @@ struct venus_format { * @vcodec1_clks: an array of vcodec1 struct clk pointers * @video_path: an interconnect handle to video to/from memory path * @cpucfg_path: an interconnect handle to cpu configuration path - * @has_opp_table: does OPP table exist * @pmdomains: a pointer to a list of pmdomains - * @opp_dl_venus: an device-link for device OPP * @opp_pmdomain: an OPP power-domain * @resets: an array of reset signals * @vdev_dec: a reference to video device structure for decoder instances @@ -186,10 +184,8 @@ struct venus_core { struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct icc_path *video_path; struct icc_path *cpucfg_path; - bool has_opp_table; struct dev_pm_domain_list *pmdomains; - struct device_link *opp_dl_venus; - struct device *opp_pmdomain; + struct dev_pm_domain_list *opp_pmdomain; struct reset_control *resets[VIDC_RESETS_NUM_MAX]; struct video_device *vdev_dec; struct video_device *vdev_enc; diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index ea8a2bd9419e..33a5a659c0ad 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -864,7 +864,6 @@ static int venc_power_v4(struct device *dev, int on) static int vcodec_domains_get(struct venus_core *core) { int ret; - struct device **opp_virt_dev; struct device *dev = core->dev; const struct venus_resources *res = core->res; struct dev_pm_domain_attach_data vcodec_data = { @@ -872,6 +871,11 @@ static int vcodec_domains_get(struct venus_core *core) .num_pd_names = res->vcodec_pmdomains_num, .pd_flags = PD_FLAG_NO_DEV_LINK, }; + struct dev_pm_domain_attach_data opp_pd_data = { + .pd_names = res->opp_pmdomain, + .num_pd_names = 1, + .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP, + }; if (!res->vcodec_pmdomains_num) goto skip_pmdomains; @@ -881,37 +885,15 @@ static int vcodec_domains_get(struct venus_core *core) return ret; skip_pmdomains: - if (!core->res->opp_pmdomain) + if (!res->opp_pmdomain) return 0; /* Attach the power domain for setting performance state */ - ret = devm_pm_opp_attach_genpd(dev, res->opp_pmdomain, &opp_virt_dev); - if (ret) - goto opp_attach_err; - - core->opp_pmdomain = *opp_virt_dev; - core->opp_dl_venus = device_link_add(dev, core->opp_pmdomain, - DL_FLAG_RPM_ACTIVE | - DL_FLAG_PM_RUNTIME | - DL_FLAG_STATELESS); - if (!core->opp_dl_venus) { - ret = -ENODEV; - goto opp_attach_err; - } + ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + if (ret < 0) + return ret; return 0; - -opp_attach_err: - return ret; -} - -static void vcodec_domains_put(struct venus_core *core) -{ - if (!core->has_opp_table) - return; - - if (core->opp_dl_venus) - device_link_del(core->opp_dl_venus); } static int core_resets_reset(struct venus_core *core) @@ -1000,9 +982,7 @@ static int core_get_v4(struct venus_core *core) if (core->res->opp_pmdomain) { ret = devm_pm_opp_of_add_table(dev); - if (!ret) { - core->has_opp_table = true; - } else if (ret != -ENODEV) { + if (ret && ret != -ENODEV) { dev_err(dev, "invalid OPP table in device tree\n"); return ret; } @@ -1013,10 +993,6 @@ static int core_get_v4(struct venus_core *core) static void core_put_v4(struct venus_core *core) { - if (legacy_binding) - return; - - vcodec_domains_put(core); } static int core_power_v4(struct venus_core *core, int on) From 9da666ee5e38e11dffd4e161b282265676152c55 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 Oct 2024 14:22:31 +0200 Subject: [PATCH 13/25] cpufreq: qcom-nvmem: Convert to dev_pm_domain_attach|detach_list() Rather than hooking up the PM domains through _opp_attach_genpd() and manually manage runtime PM for the corresponding virtual devices created by genpd during attach, let's avoid the boilerplate-code by converting into dev_pm_domain_attach|detach_list. Acked-by: Viresh Kumar Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20241002122232.194245-11-ulf.hansson@linaro.org --- drivers/cpufreq/qcom-cpufreq-nvmem.c | 80 ++++++++++------------------ 1 file changed, 27 insertions(+), 53 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index 703308fb891a..ae556d5ba231 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -52,12 +52,13 @@ struct qcom_cpufreq_match_data { struct nvmem_cell *speedbin_nvmem, char **pvs_name, struct qcom_cpufreq_drv *drv); - const char **genpd_names; + const char **pd_names; + unsigned int num_pd_names; }; struct qcom_cpufreq_drv_cpu { int opp_token; - struct device **virt_devs; + struct dev_pm_domain_list *pd_list; }; struct qcom_cpufreq_drv { @@ -395,8 +396,6 @@ static int qcom_cpufreq_ipq8074_name_version(struct device *cpu_dev, return 0; } -static const char *generic_genpd_names[] = { "perf", NULL }; - static const struct qcom_cpufreq_match_data match_data_kryo = { .get_version = qcom_cpufreq_kryo_name_version, }; @@ -407,13 +406,13 @@ static const struct qcom_cpufreq_match_data match_data_krait = { static const struct qcom_cpufreq_match_data match_data_msm8909 = { .get_version = qcom_cpufreq_simple_get_version, - .genpd_names = generic_genpd_names, + .pd_names = (const char *[]) { "perf" }, + .num_pd_names = 1, }; -static const char *qcs404_genpd_names[] = { "cpr", NULL }; - static const struct qcom_cpufreq_match_data match_data_qcs404 = { - .genpd_names = qcs404_genpd_names, + .pd_names = (const char *[]) { "cpr" }, + .num_pd_names = 1, }; static const struct qcom_cpufreq_match_data match_data_ipq6018 = { @@ -428,28 +427,16 @@ static const struct qcom_cpufreq_match_data match_data_ipq8074 = { .get_version = qcom_cpufreq_ipq8074_name_version, }; -static void qcom_cpufreq_suspend_virt_devs(struct qcom_cpufreq_drv *drv, unsigned int cpu) +static void qcom_cpufreq_suspend_pd_devs(struct qcom_cpufreq_drv *drv, unsigned int cpu) { - const char * const *name = drv->data->genpd_names; + struct dev_pm_domain_list *pd_list = drv->cpus[cpu].pd_list; int i; - if (!drv->cpus[cpu].virt_devs) + if (!pd_list) return; - for (i = 0; *name; i++, name++) - device_set_awake_path(drv->cpus[cpu].virt_devs[i]); -} - -static void qcom_cpufreq_put_virt_devs(struct qcom_cpufreq_drv *drv, unsigned int cpu) -{ - const char * const *name = drv->data->genpd_names; - int i; - - if (!drv->cpus[cpu].virt_devs) - return; - - for (i = 0; *name; i++, name++) - pm_runtime_put(drv->cpus[cpu].virt_devs[i]); + for (i = 0; i < pd_list->num_pds; i++) + device_set_awake_path(pd_list->pd_devs[i]); } static int qcom_cpufreq_probe(struct platform_device *pdev) @@ -503,7 +490,6 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) } for_each_possible_cpu(cpu) { - struct device **virt_devs = NULL; struct dev_pm_opp_config config = { .supported_hw = NULL, }; @@ -522,12 +508,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) config.prop_name = pvs_name; } - if (drv->data->genpd_names) { - config.genpd_names = drv->data->genpd_names; - config.virt_devs = &virt_devs; - } - - if (config.supported_hw || config.genpd_names) { + if (config.supported_hw) { drv->cpus[cpu].opp_token = dev_pm_opp_set_config(cpu_dev, &config); if (drv->cpus[cpu].opp_token < 0) { ret = drv->cpus[cpu].opp_token; @@ -536,25 +517,18 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) } } - if (virt_devs) { - const char * const *name = config.genpd_names; - int i, j; + if (drv->data->pd_names) { + struct dev_pm_domain_attach_data attach_data = { + .pd_names = drv->data->pd_names, + .num_pd_names = drv->data->num_pd_names, + .pd_flags = PD_FLAG_DEV_LINK_ON | + PD_FLAG_REQUIRED_OPP, + }; - for (i = 0; *name; i++, name++) { - ret = pm_runtime_resume_and_get(virt_devs[i]); - if (ret) { - dev_err(cpu_dev, "failed to resume %s: %d\n", - *name, ret); - - /* Rollback previous PM runtime calls */ - name = config.genpd_names; - for (j = 0; *name && j < i; j++, name++) - pm_runtime_put(virt_devs[j]); - - goto free_opp; - } - } - drv->cpus[cpu].virt_devs = virt_devs; + ret = dev_pm_domain_attach_list(cpu_dev, &attach_data, + &drv->cpus[cpu].pd_list); + if (ret < 0) + goto free_opp; } } @@ -570,7 +544,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) free_opp: for_each_possible_cpu(cpu) { - qcom_cpufreq_put_virt_devs(drv, cpu); + dev_pm_domain_detach_list(drv->cpus[cpu].pd_list); dev_pm_opp_clear_config(drv->cpus[cpu].opp_token); } return ret; @@ -584,7 +558,7 @@ static void qcom_cpufreq_remove(struct platform_device *pdev) platform_device_unregister(cpufreq_dt_pdev); for_each_possible_cpu(cpu) { - qcom_cpufreq_put_virt_devs(drv, cpu); + dev_pm_domain_detach_list(drv->cpus[cpu].pd_list); dev_pm_opp_clear_config(drv->cpus[cpu].opp_token); } } @@ -595,7 +569,7 @@ static int qcom_cpufreq_suspend(struct device *dev) unsigned int cpu; for_each_possible_cpu(cpu) - qcom_cpufreq_suspend_virt_devs(drv, cpu); + qcom_cpufreq_suspend_pd_devs(drv, cpu); return 0; } From d6caca30a548764f8cfd78393ea09fefdf285212 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 Oct 2024 14:22:32 +0200 Subject: [PATCH 14/25] OPP: Drop redundant *_opp_attach|detach_genpd() All users of *_opp_attach|detach_genpd(), have been converted to use dev|devm_pm_domain_attach|detach_list(), hence let's drop it along with its corresponding exported functions. Acked-by: Viresh Kumar Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20241002122232.194245-12-ulf.hansson@linaro.org --- drivers/opp/core.c | 131 +---------------------------------------- drivers/opp/opp.h | 3 +- include/linux/pm_opp.h | 38 +----------- 3 files changed, 3 insertions(+), 169 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 40e5f29a2670..0311b18319a4 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2360,119 +2360,6 @@ static void _opp_put_config_regulators_helper(struct opp_table *opp_table) opp_table->config_regulators = NULL; } -static void _opp_detach_genpd(struct opp_table *opp_table) -{ - int index; - - for (index = 0; index < opp_table->required_opp_count; index++) { - if (!opp_table->required_devs[index]) - continue; - - dev_pm_domain_detach(opp_table->required_devs[index], false); - opp_table->required_devs[index] = NULL; - } -} - -/* - * Multiple generic power domains for a device are supported with the help of - * virtual genpd devices, which are created for each consumer device - genpd - * pair. These are the device structures which are attached to the power domain - * and are required by the OPP core to set the performance state of the genpd. - * The same API also works for the case where single genpd is available and so - * we don't need to support that separately. - * - * This helper will normally be called by the consumer driver of the device - * "dev", as only that has details of the genpd names. - * - * This helper needs to be called once with a list of all genpd to attach. - * Otherwise the original device structure will be used instead by the OPP core. - * - * The order of entries in the names array must match the order in which - * "required-opps" are added in DT. - */ -static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev, - const char * const *names, struct device ***virt_devs) -{ - struct device *virt_dev, *gdev; - struct opp_table *genpd_table; - int index = 0, ret = -EINVAL; - const char * const *name = names; - - if (!opp_table->required_devs) { - dev_err(dev, "Required OPPs not available, can't attach genpd\n"); - return -EINVAL; - } - - /* Genpd core takes care of propagation to parent genpd */ - if (opp_table->is_genpd) { - dev_err(dev, "%s: Operation not supported for genpds\n", __func__); - return -EOPNOTSUPP; - } - - /* Checking only the first one is enough ? */ - if (opp_table->required_devs[0]) - return 0; - - while (*name) { - if (index >= opp_table->required_opp_count) { - dev_err(dev, "Index can't be greater than required-opp-count - 1, %s (%d : %d)\n", - *name, opp_table->required_opp_count, index); - goto err; - } - - virt_dev = dev_pm_domain_attach_by_name(dev, *name); - if (IS_ERR_OR_NULL(virt_dev)) { - ret = virt_dev ? PTR_ERR(virt_dev) : -ENODEV; - dev_err(dev, "Couldn't attach to pm_domain: %d\n", ret); - goto err; - } - - /* - * The required_opp_tables parsing is not perfect, as the OPP - * core does the parsing solely based on the DT node pointers. - * The core sets the required_opp_tables entry to the first OPP - * table in the "opp_tables" list, that matches with the node - * pointer. - * - * If the target DT OPP table is used by multiple devices and - * they all create separate instances of 'struct opp_table' from - * it, then it is possible that the required_opp_tables entry - * may be set to the incorrect sibling device. - * - * Cross check it again and fix if required. - */ - gdev = dev_to_genpd_dev(virt_dev); - if (IS_ERR(gdev)) { - ret = PTR_ERR(gdev); - goto err; - } - - genpd_table = _find_opp_table(gdev); - if (!IS_ERR(genpd_table)) { - if (genpd_table != opp_table->required_opp_tables[index]) { - dev_pm_opp_put_opp_table(opp_table->required_opp_tables[index]); - opp_table->required_opp_tables[index] = genpd_table; - } else { - dev_pm_opp_put_opp_table(genpd_table); - } - } - - opp_table->required_devs[index] = virt_dev; - index++; - name++; - } - - if (virt_devs) - *virt_devs = opp_table->required_devs; - - return 0; - -err: - _opp_detach_genpd(opp_table); - return ret; - -} - static int _opp_set_required_dev(struct opp_table *opp_table, struct device *dev, struct device *required_dev, @@ -2540,9 +2427,6 @@ static void _opp_clear_config(struct opp_config_data *data) if (data->flags & OPP_CONFIG_REQUIRED_DEV) _opp_put_required_dev(data->opp_table, data->required_dev_index); - else if (data->flags & OPP_CONFIG_GENPD) - _opp_detach_genpd(data->opp_table); - if (data->flags & OPP_CONFIG_REGULATOR) _opp_put_regulators(data->opp_table); if (data->flags & OPP_CONFIG_SUPPORTED_HW) @@ -2654,20 +2538,7 @@ int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config) data->flags |= OPP_CONFIG_REGULATOR; } - /* Attach genpds */ - if (config->genpd_names) { - if (config->required_dev) { - ret = -EINVAL; - goto err; - } - - ret = _opp_attach_genpd(opp_table, dev, config->genpd_names, - config->virt_devs); - if (ret) - goto err; - - data->flags |= OPP_CONFIG_GENPD; - } else if (config->required_dev) { + if (config->required_dev) { ret = _opp_set_required_dev(opp_table, dev, config->required_dev, config->required_dev_index); diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index 3eed6f145e90..4bdb79ffa101 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -34,8 +34,7 @@ extern struct list_head opp_tables; #define OPP_CONFIG_REGULATOR_HELPER BIT(2) #define OPP_CONFIG_PROP_NAME BIT(3) #define OPP_CONFIG_SUPPORTED_HW BIT(4) -#define OPP_CONFIG_GENPD BIT(5) -#define OPP_CONFIG_REQUIRED_DEV BIT(6) +#define OPP_CONFIG_REQUIRED_DEV BIT(5) /** * struct opp_config_data - data for set config operations diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index bc74bc69107a..568183e3e641 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -62,11 +62,7 @@ typedef int (*config_clks_t)(struct device *dev, struct opp_table *opp_table, * @supported_hw: Array of hierarchy of versions to match. * @supported_hw_count: Number of elements in the array. * @regulator_names: Array of pointers to the names of the regulator, NULL terminated. - * @genpd_names: Null terminated array of pointers containing names of genpd to - * attach. Mutually exclusive with required_dev. - * @virt_devs: Pointer to return the array of genpd virtual devices. Mutually - * exclusive with required_dev. - * @required_dev: Required OPP device. Mutually exclusive with genpd_names/virt_devs. + * @required_dev: The required OPP device. * @required_dev_index: The index of the required OPP for the @required_dev. * * This structure contains platform specific OPP configurations for the device. @@ -80,8 +76,6 @@ struct dev_pm_opp_config { const unsigned int *supported_hw; unsigned int supported_hw_count; const char * const *regulator_names; - const char * const *genpd_names; - struct device ***virt_devs; struct device *required_dev; unsigned int required_dev_index; }; @@ -677,36 +671,6 @@ static inline void dev_pm_opp_put_config_regulators(int token) dev_pm_opp_clear_config(token); } -/* genpd helpers */ -static inline int dev_pm_opp_attach_genpd(struct device *dev, - const char * const *names, - struct device ***virt_devs) -{ - struct dev_pm_opp_config config = { - .genpd_names = names, - .virt_devs = virt_devs, - }; - - return dev_pm_opp_set_config(dev, &config); -} - -static inline void dev_pm_opp_detach_genpd(int token) -{ - dev_pm_opp_clear_config(token); -} - -static inline int devm_pm_opp_attach_genpd(struct device *dev, - const char * const *names, - struct device ***virt_devs) -{ - struct dev_pm_opp_config config = { - .genpd_names = names, - .virt_devs = virt_devs, - }; - - return devm_pm_opp_set_config(dev, &config); -} - /* prop-name helpers */ static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name) { From f9eb07ea696eb200093c4f5a288acec49d823280 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 17 Oct 2024 20:52:35 +0300 Subject: [PATCH 15/25] dt-bindings: power: rpmpd: Add SAR2130P compatible Document compatible for RPMh power domain controller on SAR2130P platform. Signed-off-by: Dmitry Baryshkov Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20241017-sar2130p-rpmhpd-v1-1-f4bf7f6fd12e@linaro.org Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/power/qcom,rpmpd.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 17ad46e55529..31c2ac93f804 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -39,6 +39,7 @@ properties: - qcom,sa8155p-rpmhpd - qcom,sa8540p-rpmhpd - qcom,sa8775p-rpmhpd + - qcom,sar2130p-rpmhpd - qcom,sc7180-rpmhpd - qcom,sc7280-rpmhpd - qcom,sc8180x-rpmhpd From c16a7d3ef029494a6f5b5cd20606dd08aeaa2b0f Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Thu, 17 Oct 2024 11:51:34 +0300 Subject: [PATCH 16/25] dt-bindings: power: Add binding for MediaTek MT6735 power controller Add DT binding for MediaTek MT6735 SCPSYS power controller. Signed-off-by: Yassine Oudjana Reviewed-by: AngeloGioacchino Del Regno Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20241017085136.68053-2-y.oudjana@protonmail.com Signed-off-by: Ulf Hansson --- .../bindings/power/mediatek,power-controller.yaml | 1 + .../devicetree/bindings/soc/mediatek/scpsys.txt | 1 + .../power/mediatek,mt6735-power-controller.h | 14 ++++++++++++++ 3 files changed, 16 insertions(+) create mode 100644 include/dt-bindings/power/mediatek,mt6735-power-controller.h diff --git a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml index 8985e2df8a56..6d37c06b2f65 100644 --- a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml +++ b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml @@ -23,6 +23,7 @@ properties: compatible: enum: + - mediatek,mt6735-power-controller - mediatek,mt6795-power-controller - mediatek,mt8167-power-controller - mediatek,mt8173-power-controller diff --git a/Documentation/devicetree/bindings/soc/mediatek/scpsys.txt b/Documentation/devicetree/bindings/soc/mediatek/scpsys.txt index 2bc367793aec..3530a6668b48 100644 --- a/Documentation/devicetree/bindings/soc/mediatek/scpsys.txt +++ b/Documentation/devicetree/bindings/soc/mediatek/scpsys.txt @@ -20,6 +20,7 @@ Required properties: - compatible: Should be one of: - "mediatek,mt2701-scpsys" - "mediatek,mt2712-scpsys" + - "mediatek,mt6735-scpsys" - "mediatek,mt6765-scpsys" - "mediatek,mt6797-scpsys" - "mediatek,mt7622-scpsys" diff --git a/include/dt-bindings/power/mediatek,mt6735-power-controller.h b/include/dt-bindings/power/mediatek,mt6735-power-controller.h new file mode 100644 index 000000000000..6957075fcb9e --- /dev/null +++ b/include/dt-bindings/power/mediatek,mt6735-power-controller.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef _DT_BINDINGS_POWER_MT6735_POWER_CONTROLLER_H +#define _DT_BINDINGS_POWER_MT6735_POWER_CONTROLLER_H + +#define MT6735_POWER_DOMAIN_MD1 0 +#define MT6735_POWER_DOMAIN_CONN 1 +#define MT6735_POWER_DOMAIN_DIS 2 +#define MT6735_POWER_DOMAIN_MFG 3 +#define MT6735_POWER_DOMAIN_ISP 4 +#define MT6735_POWER_DOMAIN_VDE 5 +#define MT6735_POWER_DOMAIN_VEN 6 + +#endif From 5473cff9394082a980d456685e7e594004953c62 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 17 Oct 2024 20:52:36 +0300 Subject: [PATCH 17/25] pmdomain: qcom: rpmhpd: add support for SAR2130P Define power domains as supported by the RPMh on the SAR2130P platform. Signed-off-by: Dmitry Baryshkov Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20241017-sar2130p-rpmhpd-v1-2-f4bf7f6fd12e@linaro.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmhpd.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 731feab9f17d..5c5f9542c392 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -259,6 +259,30 @@ static const struct rpmhpd_desc sa8775p_desc = { .num_pds = ARRAY_SIZE(sa8775p_rpmhpds), }; +/* SAR2130P RPMH powerdomains */ +static struct rpmhpd *sar2130p_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MMCX] = &mmcx_w_cx_parent, + [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, + [RPMHPD_MSS] = &mss, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, + [RPMHPD_NSP] = &nsp, + [RPMHPD_QPHY] = &qphy, +}; + +static const struct rpmhpd_desc sar2130p_desc = { + .rpmhpds = sar2130p_rpmhpds, + .num_pds = ARRAY_SIZE(sar2130p_rpmhpds), +}; + /* SDM670 RPMH powerdomains */ static struct rpmhpd *sdm670_rpmhpds[] = { [SDM670_CX] = &cx_w_mx_parent, @@ -665,6 +689,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,sa8155p-rpmhpd", .data = &sa8155p_desc }, { .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc }, { .compatible = "qcom,sa8775p-rpmhpd", .data = &sa8775p_desc }, + { .compatible = "qcom,sar2130p-rpmhpd", .data = &sar2130p_desc}, { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, { .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc }, { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, From c2114a0d17631497df99388905353c11f4d5f0dd Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Thu, 17 Oct 2024 11:51:35 +0300 Subject: [PATCH 18/25] pmdomain: mediatek: Add support for MT6735 Add support for SCPSYS power domains of MT6735. All non-CPU power domains are added except for MD2 (C2K modem), which is left out due to issues with powering it on. Signed-off-by: Yassine Oudjana Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20241017085136.68053-3-y.oudjana@protonmail.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mt6735-pm-domains.h | 96 +++++++++++++++++++ drivers/pmdomain/mediatek/mtk-pm-domains.c | 5 + drivers/pmdomain/mediatek/mtk-pm-domains.h | 2 + include/linux/soc/mediatek/infracfg.h | 5 + 4 files changed, 108 insertions(+) create mode 100644 drivers/pmdomain/mediatek/mt6735-pm-domains.h diff --git a/drivers/pmdomain/mediatek/mt6735-pm-domains.h b/drivers/pmdomain/mediatek/mt6735-pm-domains.h new file mode 100644 index 000000000000..71896be68e22 --- /dev/null +++ b/drivers/pmdomain/mediatek/mt6735-pm-domains.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_MEDIATEK_MT6735_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT6735_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +#include + +/* + * MT6735 power domain support + */ + +static const struct scpsys_domain_data scpsys_domain_data_mt6735[] = { + [MT6735_POWER_DOMAIN_MD1] = { + .name = "md1", + .sta_mask = PWR_STATUS_MD1, + .ctl_offs = SPM_MD1_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = 0, + .bp_cfg = { + BUS_PROT_INFRA_UPDATE_TOPAXI(MT6735_TOP_AXI_PROT_EN_MD1), + }, + }, + [MT6735_POWER_DOMAIN_CONN] = { + .name = "conn", + .sta_mask = PWR_STATUS_CONN, + .ctl_offs = SPM_CONN_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = 0, + .bp_cfg = { + BUS_PROT_INFRA_UPDATE_TOPAXI(MT6735_TOP_AXI_PROT_EN_CONN), + }, + }, + [MT6735_POWER_DOMAIN_DIS] = { + .name = "dis", + .sta_mask = PWR_STATUS_DISP, + .ctl_offs = SPM_DIS_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_cfg = { + BUS_PROT_INFRA_UPDATE_TOPAXI(MT8173_TOP_AXI_PROT_EN_MM_M0), + }, + }, + [MT6735_POWER_DOMAIN_MFG] = { + .name = "mfg", + .sta_mask = PWR_STATUS_MFG, + .ctl_offs = SPM_MFG_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_cfg = { + BUS_PROT_INFRA_UPDATE_TOPAXI(MT8173_TOP_AXI_PROT_EN_MFG_S), + }, + }, + [MT6735_POWER_DOMAIN_ISP] = { + .name = "isp", + .sta_mask = PWR_STATUS_ISP, + .ctl_offs = SPM_ISP_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + }, + [MT6735_POWER_DOMAIN_VDE] = { + .name = "vde", + .sta_mask = PWR_STATUS_VDEC, + .ctl_offs = SPM_VDE_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT6735_POWER_DOMAIN_VEN] = { + .name = "ven", + .sta_mask = BIT(8), + .ctl_offs = SPM_VEN_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + }, +}; + +static const struct scpsys_soc_data mt6735_scpsys_data = { + .domains_data = scpsys_domain_data_mt6735, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt6735), +}; + +#endif /* __SOC_MEDIATEK_MT6735_PM_DOMAINS_H */ diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index 3580913f25d3..b866b006af69 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -16,6 +16,7 @@ #include #include +#include "mt6735-pm-domains.h" #include "mt6795-pm-domains.h" #include "mt8167-pm-domains.h" #include "mt8173-pm-domains.h" @@ -608,6 +609,10 @@ static void scpsys_domain_cleanup(struct scpsys *scpsys) } static const struct of_device_id scpsys_of_match[] = { + { + .compatible = "mediatek,mt6735-power-controller", + .data = &mt6735_scpsys_data, + }, { .compatible = "mediatek,mt6795-power-controller", .data = &mt6795_scpsys_data, diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h index aaba5e6b0536..2ac96804b985 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.h +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h @@ -21,6 +21,7 @@ #define SPM_ISP_PWR_CON 0x0238 #define SPM_DIS_PWR_CON 0x023c #define SPM_CONN_PWR_CON 0x0280 +#define SPM_MD1_PWR_CON 0x0284 #define SPM_VEN2_PWR_CON 0x0298 #define SPM_AUDIO_PWR_CON 0x029c #define SPM_MFG_2D_PWR_CON 0x02c0 @@ -30,6 +31,7 @@ #define SPM_PWR_STATUS 0x060c #define SPM_PWR_STATUS_2ND 0x0610 +#define PWR_STATUS_MD1 BIT(0) #define PWR_STATUS_CONN BIT(1) #define PWR_STATUS_DISP BIT(3) #define PWR_STATUS_MFG BIT(4) diff --git a/include/linux/soc/mediatek/infracfg.h b/include/linux/soc/mediatek/infracfg.h index 6c6cccc848f4..9956e18c5ffa 100644 --- a/include/linux/soc/mediatek/infracfg.h +++ b/include/linux/soc/mediatek/infracfg.h @@ -434,6 +434,11 @@ #define MT7622_TOP_AXI_PROT_EN_WB (BIT(2) | BIT(6) | \ BIT(7) | BIT(8)) +#define MT6735_TOP_AXI_PROT_EN_CONN (BIT(2) | BIT(8)) +#define MT6735_TOP_AXI_PROT_EN_MD1 (BIT(24) | BIT(25) | \ + BIT(26) | BIT(27) | \ + BIT(28)) + #define INFRA_TOPAXI_PROTECTEN 0x0220 #define INFRA_TOPAXI_PROTECTSTA1 0x0228 #define INFRA_TOPAXI_PROTECTEN_SET 0x0260 From 15d392281d0649e6e62427bd368159ceb4ce4334 Mon Sep 17 00:00:00 2001 From: Thomas Richard Date: Tue, 22 Oct 2024 10:23:36 +0200 Subject: [PATCH 19/25] pmdomain: ti-sci: set the GENPD_FLAG_ACTIVE_WAKEUP flag for all PM domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this flag, if a device is marked on the wakeup path, the corresponding PM domain is kept powered on. This commit fixes the no_console_suspend support for some TI platforms (tested on J7200). In case of no_console_suspend the serial core marks the device on the wakeup path, but without this patch the power domain is powered off anyway. Suggested-by: Théo Lebrun Signed-off-by: Thomas Richard Reviewed-by: Kevin Hilman Link: https://lore.kernel.org/r/20241022-8250-omap-no-console-suspend-v2-1-cc3d102b8a1e@bootlin.com Signed-off-by: Ulf Hansson --- drivers/pmdomain/ti/ti_sci_pm_domains.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pmdomain/ti/ti_sci_pm_domains.c b/drivers/pmdomain/ti/ti_sci_pm_domains.c index 1510d5ddae3d..38448b4a035a 100644 --- a/drivers/pmdomain/ti/ti_sci_pm_domains.c +++ b/drivers/pmdomain/ti/ti_sci_pm_domains.c @@ -186,6 +186,7 @@ static int ti_sci_pm_domain_probe(struct platform_device *pdev) pd->pd.power_off = ti_sci_pd_power_off; pd->pd.power_on = ti_sci_pd_power_on; + pd->pd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; pd->idx = args.args[0]; pd->parent = pd_provider; From afc2331ef81657493c074592c409dac7c3cb8ccc Mon Sep 17 00:00:00 2001 From: Zhang Zekun Date: Thu, 24 Oct 2024 11:04:41 +0800 Subject: [PATCH 20/25] pmdomain: ti-sci: Add missing of_node_put() for args.np of_parse_phandle_with_args() needs to call of_node_put() to decrement the refcount of args.np. So, Add the missing of_node_put() in the loop. Fixes: efa5c01cd7ee ("soc: ti: ti_sci_pm_domains: switch to use multiple genpds instead of one") Signed-off-by: Zhang Zekun Reviewed-by: Dhruva Gole Message-ID: <20241024030442.119506-2-zhangzekun11@huawei.com> Signed-off-by: Ulf Hansson --- drivers/pmdomain/ti/ti_sci_pm_domains.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pmdomain/ti/ti_sci_pm_domains.c b/drivers/pmdomain/ti/ti_sci_pm_domains.c index 38448b4a035a..f145f0225524 100644 --- a/drivers/pmdomain/ti/ti_sci_pm_domains.c +++ b/drivers/pmdomain/ti/ti_sci_pm_domains.c @@ -161,6 +161,7 @@ static int ti_sci_pm_domain_probe(struct platform_device *pdev) break; if (args.args_count >= 1 && args.np == dev->of_node) { + of_node_put(args.np); if (args.args[0] > max_id) { max_id = args.args[0]; } else { @@ -193,7 +194,10 @@ static int ti_sci_pm_domain_probe(struct platform_device *pdev) pm_genpd_init(&pd->pd, NULL, true); list_add(&pd->node, &pd_provider->pd_list); + } else { + of_node_put(args.np); } + index++; } } From 88e98cd5d3b98df245e439345b139745e1174f57 Mon Sep 17 00:00:00 2001 From: Zhang Zekun Date: Thu, 24 Oct 2024 11:04:42 +0800 Subject: [PATCH 21/25] pmdomain: ti-sci: Use scope based of_node_put() to simplify code. Use scope based of_node_put() to simplify the code logic, and we don't need to call of_node_put(). Besides, put of_parse_phandle_with_args() in the while loop to make code more simple. Signed-off-by: Zhang Zekun Reviewed-by: Dhruva Gole Message-ID: <20241024030442.119506-3-zhangzekun11@huawei.com> Signed-off-by: Ulf Hansson --- drivers/pmdomain/ti/ti_sci_pm_domains.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/pmdomain/ti/ti_sci_pm_domains.c b/drivers/pmdomain/ti/ti_sci_pm_domains.c index f145f0225524..0e4bd749d067 100644 --- a/drivers/pmdomain/ti/ti_sci_pm_domains.c +++ b/drivers/pmdomain/ti/ti_sci_pm_domains.c @@ -131,9 +131,8 @@ static int ti_sci_pm_domain_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct ti_sci_genpd_provider *pd_provider; struct ti_sci_pm_domain *pd; - struct device_node *np; + struct device_node *np __free(device_node) = NULL; struct of_phandle_args args; - int ret; u32 max_id = 0; int index; @@ -153,12 +152,9 @@ static int ti_sci_pm_domain_probe(struct platform_device *pdev) for_each_node_with_property(np, "power-domains") { index = 0; - while (1) { - ret = of_parse_phandle_with_args(np, "power-domains", - "#power-domain-cells", - index, &args); - if (ret) - break; + while (!of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", + index, &args)) { if (args.args_count >= 1 && args.np == dev->of_node) { of_node_put(args.np); @@ -172,18 +168,14 @@ static int ti_sci_pm_domain_probe(struct platform_device *pdev) } pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); - if (!pd) { - of_node_put(np); + if (!pd) return -ENOMEM; - } pd->pd.name = devm_kasprintf(dev, GFP_KERNEL, "pd:%d", args.args[0]); - if (!pd->pd.name) { - of_node_put(np); + if (!pd->pd.name) return -ENOMEM; - } pd->pd.power_off = ti_sci_pd_power_off; pd->pd.power_on = ti_sci_pd_power_on; From da3d242cf169a6e8a4be6eea7411c65083e6ce75 Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Fri, 25 Oct 2024 18:03:56 +0200 Subject: [PATCH 22/25] pmdomain: imx: gpcv2: replace dev_err() with dev_err_probe() The patch standardizes the probe() code by replacing the two occurrences of dev_err() with dev_err_probe(). Indeed, dev_err_probe() was used in all other error paths of the probe() function. Note that dev_err_probe() has advantages even if the error code is not EPROBE_DEFER, such as the symbolic output of the error code. Therefore, it should generally be preferred over dev_err(). Co-developed-by: Michael Trimarchi Signed-off-by: Michael Trimarchi Signed-off-by: Dario Binacchi Reviewed-by: Marco Felsch Message-ID: <20241025160430.4113467-1-dario.binacchi@amarulasolutions.com> Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/gpcv2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/imx/gpcv2.c b/drivers/pmdomain/imx/gpcv2.c index 963d61c5af6d..6e6ecbf2e152 100644 --- a/drivers/pmdomain/imx/gpcv2.c +++ b/drivers/pmdomain/imx/gpcv2.c @@ -1356,7 +1356,7 @@ static int imx_pgc_domain_probe(struct platform_device *pdev) ret = pm_genpd_init(&domain->genpd, NULL, true); if (ret) { - dev_err(domain->dev, "Failed to init power domain\n"); + dev_err_probe(domain->dev, ret, "Failed to init power domain\n"); goto out_domain_unmap; } @@ -1367,7 +1367,7 @@ static int imx_pgc_domain_probe(struct platform_device *pdev) ret = of_genpd_add_provider_simple(domain->dev->of_node, &domain->genpd); if (ret) { - dev_err(domain->dev, "Failed to add genpd provider\n"); + dev_err_probe(domain->dev, ret, "Failed to add genpd provider\n"); goto out_genpd_remove; } From 8d04dfdbc380d1b77d4312f9e6d2ac1b42598fed Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 4 Nov 2024 13:07:42 -0600 Subject: [PATCH 23/25] pmdomain: imx: Use of_property_present() for non-boolean properties The use of of_property_read_bool() for non-boolean properties is deprecated in favor of of_property_present() when testing for property presence. Signed-off-by: Rob Herring (Arm) Message-ID: <20241104190742.276710-1-robh@kernel.org> Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/gpc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/imx/gpc.c b/drivers/pmdomain/imx/gpc.c index 80a4dcc77199..fbb4c90b72c4 100644 --- a/drivers/pmdomain/imx/gpc.c +++ b/drivers/pmdomain/imx/gpc.c @@ -411,7 +411,7 @@ static int imx_gpc_probe(struct platform_device *pdev) pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc"); /* bail out if DT too old and doesn't provide the necessary info */ - if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") && + if (!of_property_present(pdev->dev.of_node, "#power-domain-cells") && !pgc_node) return 0; @@ -511,7 +511,7 @@ static void imx_gpc_remove(struct platform_device *pdev) pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc"); /* bail out if DT too old and doesn't provide the necessary info */ - if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") && + if (!of_property_present(pdev->dev.of_node, "#power-domain-cells") && !pgc_node) return; From 134e9d035d830aabd1121bcda89f7ee9a476d3a3 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Mon, 11 Nov 2024 16:24:43 -0800 Subject: [PATCH 24/25] dt-bindings: power: qcom,rpmpd: document the SM8750 RPMh Power Domains Document the RPMh Power Domains on the SM8750 Platform. Signed-off-by: Taniya Das Signed-off-by: Jishnu Prakash Signed-off-by: Melody Olvera Message-ID: <20241112002444.2802092-2-quic_molvera@quicinc.com> Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/power/qcom,rpmpd.yaml | 1 + include/dt-bindings/power/qcom-rpmpd.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 31c2ac93f804..655687369a23 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -61,6 +61,7 @@ properties: - qcom,sm8450-rpmhpd - qcom,sm8550-rpmhpd - qcom,sm8650-rpmhpd + - qcom,sm8750-rpmhpd - qcom,x1e80100-rpmhpd - items: - enum: diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h index 608087fb9a3d..df599bf46220 100644 --- a/include/dt-bindings/power/qcom-rpmpd.h +++ b/include/dt-bindings/power/qcom-rpmpd.h @@ -218,6 +218,7 @@ /* SDM845 Power Domain performance levels */ #define RPMH_REGULATOR_LEVEL_RETENTION 16 #define RPMH_REGULATOR_LEVEL_MIN_SVS 48 +#define RPMH_REGULATOR_LEVEL_LOW_SVS_D3 50 #define RPMH_REGULATOR_LEVEL_LOW_SVS_D2 52 #define RPMH_REGULATOR_LEVEL_LOW_SVS_D1 56 #define RPMH_REGULATOR_LEVEL_LOW_SVS_D0 60 @@ -238,6 +239,7 @@ #define RPMH_REGULATOR_LEVEL_TURBO_L1 416 #define RPMH_REGULATOR_LEVEL_TURBO_L2 432 #define RPMH_REGULATOR_LEVEL_TURBO_L3 448 +#define RPMH_REGULATOR_LEVEL_TURBO_L4 452 #define RPMH_REGULATOR_LEVEL_SUPER_TURBO 464 #define RPMH_REGULATOR_LEVEL_SUPER_TURBO_NO_CPR 480 From 3d25d46a255a83f94d7d4d4216f38aafc8e116b0 Mon Sep 17 00:00:00 2001 From: Jishnu Prakash Date: Mon, 11 Nov 2024 16:24:44 -0800 Subject: [PATCH 25/25] pmdomain: qcom: rpmhpd: Add rpmhpd support for SM8750 This adds the RPMHPD present in SM8750 SoCs. Signed-off-by: Jishnu Prakash Signed-off-by: Taniya Das Signed-off-by: Melody Olvera Message-ID: <20241112002444.2802092-3-quic_molvera@quicinc.com> Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmhpd.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 5c5f9542c392..dfd0f80154e4 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -537,6 +537,31 @@ static const struct rpmhpd_desc sm8650_desc = { .num_pds = ARRAY_SIZE(sm8650_rpmhpds), }; +/* SM8750 RPMH powerdomains */ +static struct rpmhpd *sm8750_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_GMXC] = &gmxc, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MMCX] = &mmcx_w_cx_parent, + [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, + [RPMHPD_MSS] = &mss, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, + [RPMHPD_NSP] = &nsp, + [RPMHPD_NSP2] = &nsp2, +}; + +static const struct rpmhpd_desc sm8750_desc = { + .rpmhpds = sm8750_rpmhpds, + .num_pds = ARRAY_SIZE(sm8750_rpmhpds), +}; + /* QDU1000/QRU1000 RPMH powerdomains */ static struct rpmhpd *qdu1000_rpmhpds[] = { [QDU1000_CX] = &cx, @@ -707,6 +732,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,sm8450-rpmhpd", .data = &sm8450_desc }, { .compatible = "qcom,sm8550-rpmhpd", .data = &sm8550_desc }, { .compatible = "qcom,sm8650-rpmhpd", .data = &sm8650_desc }, + { .compatible = "qcom,sm8750-rpmhpd", .data = &sm8750_desc }, { .compatible = "qcom,x1e80100-rpmhpd", .data = &x1e80100_desc }, { } };