summaryrefslogtreecommitdiff
path: root/drivers/opp
diff options
context:
space:
mode:
authorUlf Hansson <ulf.hansson@linaro.org>2024-10-02 15:22:24 +0300
committerUlf Hansson <ulf.hansson@linaro.org>2024-10-10 15:11:41 +0300
commit0e8158b4a82eb5bfb69df17510d210b073896f00 (patch)
treec09a7e5c782c2d9190ac38c1217c078c59a8e677 /drivers/opp
parenta821b48783c72b2402220434080583856c21c523 (diff)
downloadlinux-0e8158b4a82eb5bfb69df17510d210b073896f00.tar.xz
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 <ulf.hansson@linaro.org> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Link: https://lore.kernel.org/r/20241002122232.194245-4-ulf.hansson@linaro.org
Diffstat (limited to 'drivers/opp')
-rw-r--r--drivers/opp/core.c84
-rw-r--r--drivers/opp/opp.h4
2 files changed, 59 insertions, 29 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;
+ 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;
+ }
- 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;
+ /*
+ * 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[i] = required_devs[i];
}
+ 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;
};
/**