diff mbox series

[2/4] OPP: Add support for parsing the 'opp-sustainable' property

Message ID 20201028140847.1018-3-lukasz.luba@arm.com
State New
Headers show
Series Add sustainable OPP concept | expand

Commit Message

Lukasz Luba Oct. 28, 2020, 2:08 p.m. UTC
Now that the OPP bindings are updated to include an optional
'opp-sustainable' property, add support to parse it from device tree and
store it as part of dev_pm_opp structure.
Also add and export an helper 'dev_pm_opp_get_sustainable()' that can be
used to get the sustainable OPP when present.

Signed-off-by: Lukasz Luba <lukasz.luba@arm.com>
---
 drivers/opp/core.c     | 26 ++++++++++++++++++++++++++
 drivers/opp/of.c       | 14 ++++++++++++++
 drivers/opp/opp.h      |  3 +++
 include/linux/pm_opp.h |  6 ++++++
 4 files changed, 49 insertions(+)

Comments

Quentin Perret Oct. 30, 2020, 11:47 a.m. UTC | #1
Hi Lukasz,

On Wednesday 28 Oct 2020 at 14:08:45 (+0000), Lukasz Luba wrote:
> +unsigned long dev_pm_opp_get_sustainable_opp_freq(struct device *dev)
> +{
> +	struct opp_table *opp_table;
> +	unsigned long freq = 0;
> +
> +	opp_table = _find_opp_table(dev);
> +	if (IS_ERR(opp_table))
> +		return 0;
> +
> +	if (opp_table->sustainable_opp && opp_table->sustainable_opp->available)
> +		freq = dev_pm_opp_get_freq(opp_table->sustainable_opp);
> +
> +	dev_pm_opp_put_opp_table(opp_table);
> +
> +	return freq;
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_opp_get_sustainable_opp_freq);

I'm guessing this is what IPA will use to find out what the sustainable
frequency is right?

Is PM_OPP the right place for that? It feels odd IPA will get the EM
from one place, which includes the performance state, and the sustained
OPP from another. Should we move that to PM_EM instead?

Thanks,
Quentin
Lukasz Luba Oct. 30, 2020, 12:53 p.m. UTC | #2
Hi Quentin,

On 10/30/20 11:47 AM, Quentin Perret wrote:
> Hi Lukasz,
> 
> On Wednesday 28 Oct 2020 at 14:08:45 (+0000), Lukasz Luba wrote:
>> +unsigned long dev_pm_opp_get_sustainable_opp_freq(struct device *dev)
>> +{
>> +	struct opp_table *opp_table;
>> +	unsigned long freq = 0;
>> +
>> +	opp_table = _find_opp_table(dev);
>> +	if (IS_ERR(opp_table))
>> +		return 0;
>> +
>> +	if (opp_table->sustainable_opp && opp_table->sustainable_opp->available)
>> +		freq = dev_pm_opp_get_freq(opp_table->sustainable_opp);
>> +
>> +	dev_pm_opp_put_opp_table(opp_table);
>> +
>> +	return freq;
>> +}
>> +EXPORT_SYMBOL_GPL(dev_pm_opp_get_sustainable_opp_freq);
> 
> I'm guessing this is what IPA will use to find out what the sustainable
> frequency is right?

Yes, you are right.

> 
> Is PM_OPP the right place for that? It feels odd IPA will get the EM
> from one place, which includes the performance state, and the sustained
> OPP from another. Should we move that to PM_EM instead?

True, it might looks strange, but the OPP framework is available when we
are adding the OPPs in scmi perf layer. The EM is available after we
register the device, so at the end of scmi-cpufreq init.
It would require a new scmi perf api function e.g. get_sustained_freq(),
and a set/get function for EM, which is doable.

I've discussed this approach to Viresh and he likes it better.
I am happy that you are also suggesting the EM approach.

I will send different patches for EM and SCMI to make that happen.
Should I re-based them on top of the patch adding this milliwatts filed
in EM [1]? Or do the opposite, changing the dependency order?

Regards,
Lukasz

[1] https://lkml.org/lkml/2020/10/19/392

> 
> Thanks,
> Quentin
>
diff mbox series

Patch

diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 9915e8487f0b..bb1e68b96d0e 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -299,6 +299,32 @@  unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq);
 
+/**
+ * dev_pm_opp_get_sustainable_opp_freq() - Get frequency of sustainable opp
+ *					   in Hz
+ * @dev:	device for which we do this operation
+ *
+ * Return: This function returns the frequency of the OPP marked as
+ * sustainable_opp if one is available, else returns 0;
+ */
+unsigned long dev_pm_opp_get_sustainable_opp_freq(struct device *dev)
+{
+	struct opp_table *opp_table;
+	unsigned long freq = 0;
+
+	opp_table = _find_opp_table(dev);
+	if (IS_ERR(opp_table))
+		return 0;
+
+	if (opp_table->sustainable_opp && opp_table->sustainable_opp->available)
+		freq = dev_pm_opp_get_freq(opp_table->sustainable_opp);
+
+	dev_pm_opp_put_opp_table(opp_table);
+
+	return freq;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_sustainable_opp_freq);
+
 int _get_opp_count(struct opp_table *opp_table)
 {
 	struct dev_pm_opp *opp;
diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index 9faeb83e4b32..1f6b19bb1a95 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -815,6 +815,20 @@  static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
 		}
 	}
 
+	if (of_property_read_bool(np, "opp-sustainable")) {
+		if (opp_table->sustainable_opp) {
+			/* Pick the OPP with higher rate as sustainable OPP */
+			if (new_opp->rate > opp_table->sustainable_opp->rate) {
+				opp_table->sustainable_opp->sustainable = false;
+				new_opp->sustainable = true;
+				opp_table->sustainable_opp = new_opp;
+			}
+		} else {
+			new_opp->sustainable = true;
+			opp_table->sustainable_opp = new_opp;
+		}
+	}
+
 	if (new_opp->clock_latency_ns > opp_table->clock_latency_ns_max)
 		opp_table->clock_latency_ns_max = new_opp->clock_latency_ns;
 
diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h
index ebd930e0b3ca..45288ccbb295 100644
--- a/drivers/opp/opp.h
+++ b/drivers/opp/opp.h
@@ -56,6 +56,7 @@  extern struct list_head opp_tables;
  * @dynamic:	not-created from static DT entries.
  * @turbo:	true if turbo (boost) OPP
  * @suspend:	true if suspend OPP
+ * @sustainable: true if sustainable OPP
  * @pstate: Device's power domain's performance state.
  * @rate:	Frequency in hertz
  * @level:	Performance level
@@ -78,6 +79,7 @@  struct dev_pm_opp {
 	bool dynamic;
 	bool turbo;
 	bool suspend;
+	bool sustainable;
 	unsigned int pstate;
 	unsigned long rate;
 	unsigned int level;
@@ -183,6 +185,7 @@  struct opp_table {
 	unsigned int parsed_static_opps;
 	enum opp_table_access shared_opp;
 	struct dev_pm_opp *suspend_opp;
+	struct dev_pm_opp *sustainable_opp;
 
 	struct mutex genpd_virt_dev_lock;
 	struct device **genpd_virt_devs;
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index dbb484524f82..363d5a2c1ef3 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -106,6 +106,7 @@  unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev);
 unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev);
 unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev);
 unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev);
+unsigned long dev_pm_opp_get_sustainable_opp_freq(struct device *dev);
 
 struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
 					      unsigned long freq,
@@ -215,6 +216,11 @@  static inline unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev)
 	return 0;
 }
 
+static inline unsigned long dev_pm_opp_get_sustainable_opp_freq(struct device *dev)
+{
+	return 0;
+}
+
 static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
 					unsigned long freq, bool available)
 {