[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250717-opp_pcie-v1-1-dde6f452571b@oss.qualcomm.com>
Date: Thu, 17 Jul 2025 19:31:16 +0530
From: Krishna Chaitanya Chundru <krishna.chundru@....qualcomm.com>
To: Viresh Kumar <vireshk@...nel.org>, Nishanth Menon <nm@...com>,
Stephen Boyd <sboyd@...nel.org>,
"Rafael J. Wysocki" <rafael@...nel.org>,
Manivannan Sadhasivam <mani@...nel.org>,
Lorenzo Pieralisi <lpieralisi@...nel.org>,
Krzysztof Wilczyński <kwilczynski@...nel.org>,
Rob Herring <robh@...nel.org>, Bjorn Helgaas <bhelgaas@...gle.com>,
Bjorn Andersson <andersson@...nel.org>,
Konrad Dybcio <konradybcio@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>
Cc: linux-pm@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-pci@...r.kernel.org, linux-arm-msm@...r.kernel.org,
devicetree@...r.kernel.org,
Krishna Chaitanya Chundru <krishna.chundru@....qualcomm.com>
Subject: [PATCH 1/3] opp: Add bw_factor support to adjust bandwidth
dynamically
The existing OPP table in the device tree for PCIe is shared across
different link configurations such as data rates 8GT/s x2 and 16GT/s x1.
These configurations often operate at the same frequency, allowing them
to reuse the same OPP entries. However, 8GT/s and 16 GT/s may have
different characteristics beyond frequency—such as RPMh votes in QCOM
case, which cannot be represented accurately when sharing a single OPP.
To avoid conflicts and duplication in the device tree, we now define only
one set of OPP entries per table and introduce a new mechanism to adjust
bandwidth dynamically using a `bw_factor`.
The `bw_factor` is a multiplier applied to the average and peak bandwidth
values of an OPP entry. This allows PCIe drivers to modify the effective
bandwidth at runtime based on the actual link width without needing
separate OPP entries for each configuration.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@....qualcomm.com>
---
drivers/opp/core.c | 37 +++++++++++++++++++++++++++++++++++--
drivers/opp/opp.h | 2 ++
include/linux/pm_opp.h | 7 +++++++
3 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index edbd60501cf00dfd1957f7d19b228d1c61bbbdcc..bd618fd1a36fa9c252408beb35ac2e39bfb17ee5 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -1060,8 +1060,8 @@ static int _set_opp_bw(const struct opp_table *opp_table,
avg = 0;
peak = 0;
} else {
- avg = opp->bandwidth[i].avg;
- peak = opp->bandwidth[i].peak;
+ avg = opp->bandwidth[i].avg * opp_table->bw_factor;
+ peak = opp->bandwidth[i].peak * opp_table->bw_factor;
}
ret = icc_set_bw(opp_table->paths[i], avg, peak);
if (ret) {
@@ -1461,6 +1461,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index)
__func__, ret);
}
+ opp_table->bw_factor = 1;
BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head);
INIT_LIST_HEAD(&opp_table->opp_list);
kref_init(&opp_table->kref);
@@ -2815,6 +2816,38 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
return 0;
}
+/**
+ * dev_pm_opp_set_bw_factor() - helper to change the bw factor
+ * @dev: device for which we do this operation
+ * @bw_factor: bw factor which multiples the supplied bw
+ *
+ * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
+ * copy operation, returns 0 if no modifcation was done OR modification was
+ * successful.
+ */
+int dev_pm_opp_set_bw_factor(struct device *dev, u8 bw_factor)
+{
+ struct opp_table *opp_table __free(put_opp_table);
+ int r;
+
+ /* Find the opp_table */
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ r = PTR_ERR(opp_table);
+ dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
+ return r;
+ }
+
+ if (opp_table->bw_factor == bw_factor)
+ return 0;
+
+ scoped_guard(mutex, &opp_table->lock)
+ opp_table->bw_factor = bw_factor;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_set_bw_factor);
+
/**
* dev_pm_opp_adjust_voltage() - helper to change the voltage of an OPP
* @dev: device for which we do this operation
diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h
index 9eba63e01a9e7650cf2e49515b70ba73f72210fc..f52d8582b705f1dcf8b5c8279716d38acb273a6c 100644
--- a/drivers/opp/opp.h
+++ b/drivers/opp/opp.h
@@ -192,6 +192,7 @@ enum opp_table_access {
* property).
* @paths: Interconnect path handles
* @path_count: Number of interconnect paths
+ * @bw_factor: Multiplier to the supplied bw
* @enabled: Set to true if the device's resources are enabled/configured.
* @is_genpd: Marks if the OPP table belongs to a genpd.
* @dentry: debugfs dentry pointer of the real device directory (not links).
@@ -240,6 +241,7 @@ struct opp_table {
int regulator_count;
struct icc_path **paths;
unsigned int path_count;
+ u8 bw_factor;
bool enabled;
bool is_genpd;
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index cf477beae4bbede88223566df5f43d85adc5a816..4b090fd7391975ab3fa9a94e939325de946cadfa 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -170,6 +170,8 @@ int dev_pm_opp_add_dynamic(struct device *dev, struct dev_pm_opp_data *opp);
void dev_pm_opp_remove(struct device *dev, unsigned long freq);
void dev_pm_opp_remove_all_dynamic(struct device *dev);
+int dev_pm_opp_set_bw_factor(struct device *dev, u8 bw_factor);
+
int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
unsigned long u_volt, unsigned long u_volt_min,
unsigned long u_volt_max);
@@ -371,6 +373,11 @@ static inline void dev_pm_opp_remove_all_dynamic(struct device *dev)
{
}
+static inline int dev_pm_opp_set_bw_factor(struct device *dev, u8 bw_factor)
+{
+ return 0;
+}
+
static inline int
dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
unsigned long u_volt, unsigned long u_volt_min,
--
2.34.1
Powered by blists - more mailing lists