linux-pm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver
@ 2019-06-08  4:43 Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 1/9] dt-bindings: opp: Introduce opp-peak-KBps and opp-avg-KBps bindings Saravana Kannan
                   ` (9 more replies)
  0 siblings, 10 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-08  4:43 UTC (permalink / raw)
  To: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
  Cc: Saravana Kannan, Rajendra Nayak, Jordan Crouse, vincent.guittot,
	bjorn.andersson, amit.kucheria, seansw, daidavid1, evgreen,
	sibis, kernel-team, linux-pm, devicetree, linux-kernel

I replied[1] to this patch series[2] and described how I think interconnect
bandwidth voting should be captured in DT and how it should work.

So sending out a patch series implementing that. This patch series does the
following:
- Adds Bandwidth OPP table support (this adds device freq to bandwidth
  mapping for free)
- Adds a devfreq library for interconnect paths

Interconnects and interconnect paths quantify they performance levels in
terms of bandwidth. So similar to how we have frequency based OPP tables
in DT and in the OPP framework, this patch series adds bandwidth OPP
table support in the OPP framework and in DT.

To simplify voting for interconnects, this patch series adds helper
functions to create devfreq devices out of interconnect paths. This
allows drivers to add a single line of code to add interconnect voting
capability.

To add devfreq device for the "gpu-mem" interconnect path:
icc_create_devfreq(dev, "gpu-mem");

With the future addition of a "passive_bandwidth" devfreq governor,
device frequency to interconnect bandwidth mapping would come for free.

If the feedback on this patch series is positive, I'll then add the
devfreq passive_bandwidth governor (or something similar) to v2 of this
patch series.

So with the DT bindings added in this patch series, the DT for a GPU
that does bandwidth voting from GPU to Cache and GPU to DDR would look
something like this:

gpu_cache_opp_table: gpu_cache_opp_table {
	compatible = "operating-points-v2";

	gpu_cache_3000: opp-3000 {
		opp-peak-KBps = <3000>;
		opp-avg-KBps = <1000>;
	};
	gpu_cache_6000: opp-6000 {
		opp-peak-KBps = <6000>;
		opp-avg-KBps = <2000>;
	};
	gpu_cache_9000: opp-9000 {
		opp-peak-KBps = <9000>;
		opp-avg-KBps = <9000>;
	};
};

gpu_ddr_opp_table: gpu_ddr_opp_table {
	compatible = "operating-points-v2";

	gpu_ddr_1525: opp-1525 {
		opp-peak-KBps = <1525>;
		opp-avg-KBps = <452>;
	};
	gpu_ddr_3051: opp-3051 {
		opp-peak-KBps = <3051>;
		opp-avg-KBps = <915>;
	};
	gpu_ddr_7500: opp-7500 {
		opp-peak-KBps = <7500>;
		opp-avg-KBps = <3000>;
	};
};

gpu_opp_table: gpu_opp_table {
	compatible = "operating-points-v2";
	opp-shared;

	opp-200000000 {
		opp-hz = /bits/ 64 <200000000>;
		required-opps = <&gpu_cache_3000>, <&gpu_ddr_1525>;
	};
	opp-400000000 {
		opp-hz = /bits/ 64 <400000000>;
		required-opps = <&gpu_cache_6000>, <&gpu_ddr_3051>;
	};
};

gpu@7864000 {
	...
	operating-points-v2 = <&gpu_opp_table>, <&gpu_cache_opp_table>, <&gpu_ddr_opp_table>;
	interconnects = <&mmnoc MASTER_GPU_1 &bimc SLAVE_SYSTEL_CACHE>,
			<&mmnoc MASTER_GPU_1 &bimc SLAVE_DDR>;
	interconnect-names = "gpu-cache", "gpu-mem";
	interconnect-opp-table = <&gpu_cache_opp_table>, <&gpu_ddr_opp_table>
};

Cheers,
Saravana

[1] - https://lore.kernel.org/lkml/20190601021228.210574-1-saravanak@google.com/
[2] - https://lore.kernel.org/lkml/20190423132823.7915-1-georgi.djakov@linaro.org/ 

Saravana Kannan (9):
  dt-bindings: opp: Introduce opp-peak-KBps and opp-avg-KBps bindings
  OPP: Add support for bandwidth OPP tables
  OPP: Add helper function for bandwidth OPP tables
  OPP: Add API to find an OPP table from its DT node
  dt-bindings: interconnect: Add interconnect-opp-table property
  interconnect: Add OPP table support for interconnects
  OPP: Add function to look up required OPP's for a given OPP
  OPP: Allow copying OPPs tables between devices
  interconnect: Add devfreq support

 .../bindings/interconnect/interconnect.txt    |   8 +
 Documentation/devicetree/bindings/opp/opp.txt |  15 +-
 drivers/interconnect/Makefile                 |   2 +-
 drivers/interconnect/core.c                   |  27 +++-
 drivers/interconnect/icc-devfreq.c            | 144 ++++++++++++++++++
 drivers/opp/core.c                            | 109 +++++++++++++
 drivers/opp/of.c                              |  75 +++++++--
 drivers/opp/opp.h                             |   4 +-
 include/linux/interconnect.h                  |  17 +++
 include/linux/pm_opp.h                        |  41 +++++
 10 files changed, 426 insertions(+), 16 deletions(-)
 create mode 100644 drivers/interconnect/icc-devfreq.c

-- 
2.22.0.rc2.383.gf4fbbf30c2-goog


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v1 1/9] dt-bindings: opp: Introduce opp-peak-KBps and opp-avg-KBps bindings
  2019-06-08  4:43 [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Saravana Kannan
@ 2019-06-08  4:43 ` Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 2/9] OPP: Add support for bandwidth OPP tables Saravana Kannan
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-08  4:43 UTC (permalink / raw)
  To: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
  Cc: Saravana Kannan, Rajendra Nayak, Jordan Crouse, vincent.guittot,
	bjorn.andersson, amit.kucheria, seansw, daidavid1, evgreen,
	sibis, kernel-team, linux-pm, devicetree, linux-kernel

Interconnects often quantify their performance points in terms of
bandwidth. So, add opp-peak-KBps (required) and opp-avg-KBps (optional) to
allow specifying Bandwidth OPP tables in DT.

opp-peak-KBps is a required property that replace opp-hz for Bandwidth OPP
tables.

opp-avg-KBps is an optional property that can be used in Bandwidth OPP
tables.

Signed-off-by: Saravana Kannan <saravanak@google.com>
---
 Documentation/devicetree/bindings/opp/opp.txt | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/opp/opp.txt b/Documentation/devicetree/bindings/opp/opp.txt
index 76b6c79604a5..c869e87caa2a 100644
--- a/Documentation/devicetree/bindings/opp/opp.txt
+++ b/Documentation/devicetree/bindings/opp/opp.txt
@@ -83,9 +83,14 @@ properties.
 
 Required properties:
 - opp-hz: Frequency in Hz, expressed as a 64-bit big-endian integer. This is a
-  required property for all device nodes but devices like power domains. The
-  power domain nodes must have another (implementation dependent) property which
-  uniquely identifies the OPP nodes.
+  required property for all device nodes but for devices like power domains or
+  bandwidth opp tables. The power domain nodes must have another (implementation
+  dependent) property which uniquely identifies the OPP nodes. The interconnect
+  opps are required to have the opp-peak-bw property.
+
+- opp-peak-KBps: Peak bandwidth in kilobytes per second, expressed as a 32-bit
+  big-endian integer. This is a required property for all devices that don't
+  have opp-hz. For example, bandwidth OPP tables for interconnect paths.
 
 Optional properties:
 - opp-microvolt: voltage in micro Volts.
@@ -132,6 +137,10 @@ Optional properties:
 - opp-level: A value representing the performance level of the device,
   expressed as a 32-bit integer.
 
+- opp-avg-KBps: Average bandwidth in kilobytes per second, expressed as a
+  32-bit big-endian integer. This property is only meaningful in OPP tables
+  where opp-peak-KBps is present.
+
 - clock-latency-ns: Specifies the maximum possible transition latency (in
   nanoseconds) for switching to this OPP from any other OPP.
 
-- 
2.22.0.rc2.383.gf4fbbf30c2-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v1 2/9] OPP: Add support for bandwidth OPP tables
  2019-06-08  4:43 [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 1/9] dt-bindings: opp: Introduce opp-peak-KBps and opp-avg-KBps bindings Saravana Kannan
@ 2019-06-08  4:43 ` Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 3/9] OPP: Add helper function " Saravana Kannan
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-08  4:43 UTC (permalink / raw)
  To: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
  Cc: Saravana Kannan, Rajendra Nayak, Jordan Crouse, vincent.guittot,
	bjorn.andersson, amit.kucheria, seansw, daidavid1, evgreen,
	sibis, kernel-team, linux-pm, devicetree, linux-kernel

Not all devices quantify their performance points in terms of frequency.
Devices like interconnects quantify their performance points in terms of
bandwidth. We need a way to represent these bandwidth levels in OPP. So,
add support for parsing bandwidth OPPs from DT.

Signed-off-by: Saravana Kannan <saravanak@google.com>
---
 drivers/opp/of.c  | 34 ++++++++++++++++++++++++++++++++--
 drivers/opp/opp.h |  4 +++-
 2 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index c10c782d15aa..54fa70ed2adc 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -552,6 +552,35 @@ void dev_pm_opp_of_remove_table(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
 
+static int _read_opp_key(struct dev_pm_opp *new_opp, struct device_node *np)
+{
+	int ret;
+	u64 rate;
+	u32 bw;
+
+	ret = of_property_read_u64(np, "opp-hz", &rate);
+	if (!ret) {
+		/*
+		 * Rate is defined as an unsigned long in clk API, and so
+		 * casting explicitly to its type. Must be fixed once rate is 64
+		 * bit guaranteed in clk API.
+		 */
+		new_opp->rate = (unsigned long)rate;
+		return 0;
+	}
+
+	ret = of_property_read_u32(np, "opp-peak-KBps", &bw);
+	if (ret)
+		return ret;
+	new_opp->rate = (unsigned long) &bw;
+
+	ret = of_property_read_u32(np, "opp-avg-KBps", &bw);
+	if (!ret)
+		new_opp->avg_bw = (unsigned long) &bw;
+
+	return 0;
+}
+
 /**
  * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
  * @opp_table:	OPP table
@@ -589,11 +618,12 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
 	if (!new_opp)
 		return ERR_PTR(-ENOMEM);
 
-	ret = of_property_read_u64(np, "opp-hz", &rate);
+	ret = _read_opp_key(new_opp, np);
 	if (ret < 0) {
 		/* "opp-hz" is optional for devices like power domains. */
 		if (!opp_table->is_genpd) {
-			dev_err(dev, "%s: opp-hz not found\n", __func__);
+			dev_err(dev, "%s: opp-hz or opp-peak-bw not found\n",
+				__func__);
 			goto free_opp;
 		}
 
diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h
index 569b3525aa67..ead2cdafe957 100644
--- a/drivers/opp/opp.h
+++ b/drivers/opp/opp.h
@@ -59,7 +59,8 @@ extern struct list_head opp_tables;
  * @turbo:	true if turbo (boost) OPP
  * @suspend:	true if suspend OPP
  * @pstate: Device's power domain's performance state.
- * @rate:	Frequency in hertz
+ * @rate:	Frequency in hertz OR Peak bandwidth in kilobytes per second
+ * @avg_bw:	Average bandwidth in kilobytes per second
  * @level:	Performance level
  * @supplies:	Power supplies voltage/current values
  * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
@@ -81,6 +82,7 @@ struct dev_pm_opp {
 	bool suspend;
 	unsigned int pstate;
 	unsigned long rate;
+	unsigned long avg_bw;
 	unsigned int level;
 
 	struct dev_pm_opp_supply *supplies;
-- 
2.22.0.rc2.383.gf4fbbf30c2-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v1 3/9] OPP: Add helper function for bandwidth OPP tables
  2019-06-08  4:43 [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 1/9] dt-bindings: opp: Introduce opp-peak-KBps and opp-avg-KBps bindings Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 2/9] OPP: Add support for bandwidth OPP tables Saravana Kannan
@ 2019-06-08  4:43 ` Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 4/9] OPP: Add API to find an OPP table from its DT node Saravana Kannan
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-08  4:43 UTC (permalink / raw)
  To: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
  Cc: Saravana Kannan, Rajendra Nayak, Jordan Crouse, vincent.guittot,
	bjorn.andersson, amit.kucheria, seansw, daidavid1, evgreen,
	sibis, kernel-team, linux-pm, devicetree, linux-kernel

The frequency OPP tables have helper functions to search for entries in the
table based on frequency and get the frequency values for a given (or
suspend) OPP entry.

Add similar helper functions for bandwidth OPP tables to search for entries
in the table based on peak bandwidth and to get the peak and average
bandwidth for a given (or suspend) OPP entry.

Signed-off-by: Saravana Kannan <saravanak@google.com>
---
 drivers/opp/core.c     | 51 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_opp.h | 19 ++++++++++++++++
 2 files changed, 70 insertions(+)

diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 0e7703fe733f..89a2ece88480 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -130,6 +130,29 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp)
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq);
 
+/**
+ * dev_pm_opp_get_bw() - Gets the bandwidth corresponding to an available opp
+ * @opp:	opp for which frequency has to be returned for
+ * @avg_bw:	Pointer where the corresponding average bandwidth is stored.
+ *		Can be NULL.
+ *
+ * Return: Peak bandwidth in KBps corresponding to the opp, else
+ * return 0
+ */
+unsigned long dev_pm_opp_get_bw(struct dev_pm_opp *opp, unsigned long *avg_bw)
+{
+	if (IS_ERR_OR_NULL(opp) || !opp->available) {
+		pr_err("%s: Invalid parameters\n", __func__);
+		return 0;
+	}
+
+	if (avg_bw)
+		avg_bw = opp->avg_bw;
+
+	return opp->rate;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_bw);
+
 /**
  * dev_pm_opp_get_level() - Gets the level corresponding to an available opp
  * @opp:	opp for which level value has to be returned for
@@ -302,6 +325,34 @@ 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_suspend_opp_bw() - Get peak bandwidth of suspend opp in KBps
+ * @dev:	device for which we do this operation
+ * @avg_bw:	Pointer where the corresponding average bandwidth is stored.
+ *		Can be NULL.
+ *
+ * Return: This function returns the peak bandwidth of the OPP marked as
+ * suspend_opp if one is available, else returns 0;
+ */
+unsigned long dev_pm_opp_get_suspend_opp_bw(struct device *dev,
+					    unsigned long avg_bw)
+{
+	struct opp_table *opp_table;
+	unsigned long peak_bw = 0;
+
+	opp_table = _find_opp_table(dev);
+	if (IS_ERR(opp_table))
+		return 0;
+
+	if (opp_table->suspend_opp && opp_table->suspend_opp->available)
+		peak_bw = dev_pm_opp_get_bw(opp_table->suspend_opp, avg_bw);
+
+	dev_pm_opp_put_opp_table(opp_table);
+
+	return peak_bw;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_bw);
+
 int _get_opp_count(struct opp_table *opp_table)
 {
 	struct dev_pm_opp *opp;
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index b150fe97ce5a..7f040cc20daf 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -85,6 +85,7 @@ void dev_pm_opp_put_opp_table(struct opp_table *opp_table);
 unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp);
 
 unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp);
+unsigned long dev_pm_opp_get_bw(struct dev_pm_opp *opp, unsigned long *avg_bw);
 
 unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp);
 
@@ -95,6 +96,8 @@ 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_suspend_opp_bw(struct device *dev,
+					    unsigned long avg_bw);
 
 struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
 					      unsigned long freq,
@@ -161,6 +164,11 @@ static inline unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp)
 {
 	return 0;
 }
+static inline unsigned long dev_pm_opp_get_bw(struct dev_pm_opp *opp,
+					      unsigned long avg_bw)
+{
+	return 0;
+}
 
 static inline unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp)
 {
@@ -197,6 +205,12 @@ static inline unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev)
 	return 0;
 }
 
+static inline unsigned long dev_pm_opp_get_suspend_opp_bw(struct device *dev,
+							  unsigned long avg_bw)
+{
+	return 0;
+}
+
 static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
 					unsigned long freq, bool available)
 {
@@ -332,6 +346,11 @@ static inline void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask
 
 #endif		/* CONFIG_PM_OPP */
 
+#define dev_pm_opp_find_peak_bw_exact	dev_pm_opp_find_freq_exact
+#define dev_pm_opp_find_peak_bw_floor	dev_pm_opp_find_freq_floor
+#define dev_pm_opp_find_peak_bw_ceil_by_volt dev_pm_opp_find_freq_ceil_by_volt
+#define dev_pm_opp_find_peak_bw_ceil	dev_pm_opp_find_freq_ceil
+
 #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
 int dev_pm_opp_of_add_table(struct device *dev);
 int dev_pm_opp_of_add_table_indexed(struct device *dev, int index);
-- 
2.22.0.rc2.383.gf4fbbf30c2-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v1 4/9] OPP: Add API to find an OPP table from its DT node
  2019-06-08  4:43 [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Saravana Kannan
                   ` (2 preceding siblings ...)
  2019-06-08  4:43 ` [PATCH v1 3/9] OPP: Add helper function " Saravana Kannan
@ 2019-06-08  4:43 ` Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 5/9] dt-bindings: interconnect: Add interconnect-opp-table property Saravana Kannan
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-08  4:43 UTC (permalink / raw)
  To: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
  Cc: Saravana Kannan, Rajendra Nayak, Jordan Crouse, vincent.guittot,
	bjorn.andersson, amit.kucheria, seansw, daidavid1, evgreen,
	sibis, kernel-team, linux-pm, devicetree, linux-kernel

This allows finding a device's OPP table (when it has multiple) from a
phandle to the OPP table in DT.

Signed-off-by: Saravana Kannan <saravanak@google.com>
---
 drivers/opp/of.c       | 42 ++++++++++++++++++++++++++++++++++--------
 include/linux/pm_opp.h |  7 +++++++
 2 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index 54fa70ed2adc..34c51905f56d 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -42,14 +42,9 @@ struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node);
 
-struct opp_table *_managed_opp(struct device *dev, int index)
+struct opp_table *_find_opp_table_from_node(struct device_node *np)
 {
 	struct opp_table *opp_table, *managed_table = NULL;
-	struct device_node *np;
-
-	np = _opp_of_get_opp_desc_node(dev->of_node, index);
-	if (!np)
-		return NULL;
 
 	list_for_each_entry(opp_table, &opp_tables, node) {
 		if (opp_table->np == np) {
@@ -69,11 +64,42 @@ struct opp_table *_managed_opp(struct device *dev, int index)
 		}
 	}
 
-	of_node_put(np);
-
 	return managed_table;
 }
 
+/**
+ * dev_pm_opp_of_find_table_from_node() - Find OPP table from its DT node
+ * @np: DT node used for finding the OPP table
+ *
+ * Return: OPP table corresponding to the DT node, else NULL on failure.
+ *
+ * The caller needs to put the node with of_node_put() after using it.
+ */
+struct opp_table *dev_pm_opp_of_find_table_from_node(struct device_node *np)
+{
+	struct opp_table *opp_table;
+
+	mutex_lock(&opp_table_lock);
+	opp_table = _find_opp_table_from_node(np);
+	mutex_unlock(&opp_table_lock);
+	return opp_table;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_of_find_table_from_node);
+
+struct opp_table *_managed_opp(struct device *dev, int index)
+{
+	struct device_node *np;
+	struct opp_table *opp_table;
+
+	np = _opp_of_get_opp_desc_node(dev->of_node, index);
+	if (!np)
+		return NULL;
+
+	opp_table = _find_opp_table_from_node(np);
+	of_node_put(np);
+	return opp_table;
+}
+
 /* The caller must call dev_pm_opp_put() after the OPP is used */
 static struct dev_pm_opp *_find_opp_of_np(struct opp_table *opp_table,
 					  struct device_node *opp_np)
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 7f040cc20daf..3f30ce55e8eb 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -359,6 +359,7 @@ int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask);
 void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask);
 int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
 struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev);
+struct opp_table *dev_pm_opp_of_find_table_from_node(struct device_node *np);
 struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp);
 int of_get_required_opp_performance_state(struct device_node *np, int index);
 void dev_pm_opp_of_register_em(struct cpumask *cpus);
@@ -396,6 +397,12 @@ static inline struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device
 	return NULL;
 }
 
+static inline struct opp_table *dev_pm_opp_of_find_table_from_node(
+							struct device_node *np)
+{
+	return NULL;
+}
+
 static inline struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp)
 {
 	return NULL;
-- 
2.22.0.rc2.383.gf4fbbf30c2-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v1 5/9] dt-bindings: interconnect: Add interconnect-opp-table property
  2019-06-08  4:43 [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Saravana Kannan
                   ` (3 preceding siblings ...)
  2019-06-08  4:43 ` [PATCH v1 4/9] OPP: Add API to find an OPP table from its DT node Saravana Kannan
@ 2019-06-08  4:43 ` Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 6/9] interconnect: Add OPP table support for interconnects Saravana Kannan
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-08  4:43 UTC (permalink / raw)
  To: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
  Cc: Saravana Kannan, Rajendra Nayak, Jordan Crouse, vincent.guittot,
	bjorn.andersson, amit.kucheria, seansw, daidavid1, evgreen,
	sibis, kernel-team, linux-pm, devicetree, linux-kernel

Add support for listing bandwidth OPP tables for each interconnect path
listed using the interconnects property.

Signed-off-by: Saravana Kannan <saravanak@google.com>
---
 .../devicetree/bindings/interconnect/interconnect.txt     | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/interconnect/interconnect.txt b/Documentation/devicetree/bindings/interconnect/interconnect.txt
index 6f5d23a605b7..fc5b75b76a2c 100644
--- a/Documentation/devicetree/bindings/interconnect/interconnect.txt
+++ b/Documentation/devicetree/bindings/interconnect/interconnect.txt
@@ -55,10 +55,18 @@ interconnect-names : List of interconnect path name strings sorted in the same
 			 * dma-mem: Path from the device to the main memory of
 			            the system
 
+interconnect-opp-table: List of phandles to OPP tables (bandwidth OPP tables)
+			that specify the OPPs for the interconnect paths listed
+			in the interconnects property. This property can only
+			point to OPP tables that belong to the device and are
+			listed in the device's operating-points-v2 property.
+
 Example:
 
 	sdhci@7864000 {
+		operating-points-v2 = <&sdhc_opp_table>, <&sdhc_mem_opp_table>;
 		...
 		interconnects = <&pnoc MASTER_SDCC_1 &bimc SLAVE_EBI_CH0>;
 		interconnect-names = "sdhc-mem";
+		interconnect-opp-table = <&sdhc_mem_opp_table>;
 	};
-- 
2.22.0.rc2.383.gf4fbbf30c2-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v1 6/9] interconnect: Add OPP table support for interconnects
  2019-06-08  4:43 [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Saravana Kannan
                   ` (4 preceding siblings ...)
  2019-06-08  4:43 ` [PATCH v1 5/9] dt-bindings: interconnect: Add interconnect-opp-table property Saravana Kannan
@ 2019-06-08  4:43 ` Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 7/9] OPP: Add function to look up required OPP's for a given OPP Saravana Kannan
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-08  4:43 UTC (permalink / raw)
  To: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
  Cc: Saravana Kannan, Rajendra Nayak, Jordan Crouse, vincent.guittot,
	bjorn.andersson, amit.kucheria, seansw, daidavid1, evgreen,
	sibis, kernel-team, linux-pm, devicetree, linux-kernel

Interconnect paths can have different performance points. Now that OPP
framework supports bandwidth OPP tables, add OPP table support for
interconnects.

Devices can use the interconnect-opp-table DT property to specify OPP
tables for interconnect paths. And the driver can obtain the OPP table for
an interconnect path by calling icc_get_opp_table().

Signed-off-by: Saravana Kannan <saravanak@google.com>
---
 drivers/interconnect/core.c  | 27 ++++++++++++++++++++++++++-
 include/linux/interconnect.h |  7 +++++++
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
index 871eb4bc4efc..881bac80bc1e 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -47,6 +47,7 @@ struct icc_req {
  */
 struct icc_path {
 	size_t num_nodes;
+	struct opp_table *opp_table;
 	struct icc_req reqs[];
 };
 
@@ -313,7 +314,7 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
 {
 	struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
 	struct icc_node *src_node, *dst_node;
-	struct device_node *np = NULL;
+	struct device_node *np = NULL, *opp_node;
 	struct of_phandle_args src_args, dst_args;
 	int idx = 0;
 	int ret;
@@ -381,10 +382,34 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
 		dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
 	mutex_unlock(&icc_lock);
 
+	opp_node = of_parse_phandle(np, "interconnect-opp-table", idx);
+	if (opp_node) {
+		path->opp_table = dev_pm_opp_of_find_table_from_node(opp_node);
+		of_node_put(opp_node);
+	}
+
+
 	return path;
 }
 EXPORT_SYMBOL_GPL(of_icc_get);
 
+/**
+ * icc_get_opp_table() - Get the OPP table that corresponds to a path
+ * @path: reference to the path returned by icc_get()
+ *
+ * This function will return the OPP table that corresponds to a path handle.
+ * If the interconnect API is disabled, NULL is returned and the consumer
+ * drivers will still build. Drivers are free to handle this specifically, but
+ * they don't have to.
+ *
+ * Return: opp_table pointer on success. NULL is returned when the API is
+ * disabled or the OPP table is missing.
+ */
+struct opp_table *icc_get_opp_table(struct icc_path *path)
+{
+	return path->opp_table;
+}
+
 /**
  * icc_set_bw() - set bandwidth constraints on an interconnect path
  * @path: reference to the path returned by icc_get()
diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h
index dc25864755ba..0c0bc55f0e89 100644
--- a/include/linux/interconnect.h
+++ b/include/linux/interconnect.h
@@ -9,6 +9,7 @@
 
 #include <linux/mutex.h>
 #include <linux/types.h>
+#include <linux/pm_opp.h>
 
 /* macros for converting to icc units */
 #define Bps_to_icc(x)	((x) / 1000)
@@ -28,6 +29,7 @@ struct device;
 struct icc_path *icc_get(struct device *dev, const int src_id,
 			 const int dst_id);
 struct icc_path *of_icc_get(struct device *dev, const char *name);
+struct opp_table *icc_get_opp_table(struct icc_path *path);
 void icc_put(struct icc_path *path);
 int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw);
 
@@ -49,6 +51,11 @@ static inline void icc_put(struct icc_path *path)
 {
 }
 
+static inline struct opp_table *icc_get_opp_table(struct icc_path *path)
+{
+	return NULL;
+}
+
 static inline int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
 {
 	return 0;
-- 
2.22.0.rc2.383.gf4fbbf30c2-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v1 7/9] OPP: Add function to look up required OPP's for a given OPP
  2019-06-08  4:43 [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Saravana Kannan
                   ` (5 preceding siblings ...)
  2019-06-08  4:43 ` [PATCH v1 6/9] interconnect: Add OPP table support for interconnects Saravana Kannan
@ 2019-06-08  4:43 ` Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 8/9] OPP: Allow copying OPPs tables between devices Saravana Kannan
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-08  4:43 UTC (permalink / raw)
  To: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
  Cc: Saravana Kannan, Rajendra Nayak, Jordan Crouse, vincent.guittot,
	bjorn.andersson, amit.kucheria, seansw, daidavid1, evgreen,
	sibis, kernel-team, linux-pm, devicetree, linux-kernel

Add a function that allows looking up required OPPs given a source OPP
table, destination OPP table and the source OPP.

Signed-off-by: Saravana Kannan <saravanak@google.com>
---
 drivers/opp/core.c     | 50 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_opp.h | 11 ++++++++++
 2 files changed, 61 insertions(+)

diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 89a2ece88480..d96d5746eb47 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -1881,6 +1881,56 @@ void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table,
 		dev_err(virt_dev, "Failed to find required device entry\n");
 }
 
+/**
+ * dev_pm_opp_xlate_opp() - Find required OPP for src_table OPP.
+ * @src_table: OPP table which has dst_table as one of its required OPP table.
+ * @dst_table: Required OPP table of the src_table.
+ * @pstate: OPP of the src_table.
+ *
+ * This function returns the OPP (present in @dst_table) pointed out by the
+ * "required-opps" property of the OPP (present in @src_table).
+ *
+ * Return: destination table OPP on success, otherwise NULL on errors.
+ */
+struct dev_pm_opp *dev_pm_opp_xlate_opp(struct opp_table *src_table,
+					struct opp_table *dst_table,
+					struct dev_pm_opp *src_opp)
+{
+	struct dev_pm_opp *opp, *dest_opp = NULL;
+	int i;
+
+	if (!src_table || !dst_table || !src_opp)
+		return NULL;
+
+	for (i = 0; i < src_table->required_opp_count; i++) {
+		if (src_table->required_opp_tables[i]->np == dst_table->np)
+			break;
+	}
+
+	if (unlikely(i == src_table->required_opp_count)) {
+		pr_err("%s: Couldn't find matching OPP table (%p: %p)\n",
+		       __func__, src_table, dst_table);
+		return NULL;
+	}
+
+	mutex_lock(&src_table->lock);
+
+	list_for_each_entry(opp, &src_table->opp_list, node) {
+		if (opp == src_opp) {
+			dest_opp = opp->required_opps[i];
+			goto unlock;
+		}
+	}
+
+	pr_err("%s: Couldn't find matching OPP (%p: %p)\n", __func__, src_table,
+	       dst_table);
+
+unlock:
+	mutex_unlock(&src_table->lock);
+
+	return dest_opp;
+}
+
 /**
  * dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table.
  * @src_table: OPP table which has dst_table as one of its required OPP table.
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 3f30ce55e8eb..13381dc3dd39 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -137,6 +137,9 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
 struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index);
 void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev);
 int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
+struct dev_pm_opp *dev_pm_opp_xlate_opp(struct opp_table *src_table,
+					struct opp_table *dst_table,
+					struct dev_pm_opp *src_opp);
 int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
 int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
 int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
@@ -321,6 +324,14 @@ static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table
 	return -ENOTSUPP;
 }
 
+static inline struct dev_pm_opp *dev_pm_opp_xlate_opp(
+						struct opp_table *src_table,
+						struct opp_table *dst_table,
+						struct dev_pm_opp *src_opp)
+{
+	return NULL;
+}
+
 static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
 {
 	return -ENOTSUPP;
-- 
2.22.0.rc2.383.gf4fbbf30c2-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v1 8/9] OPP: Allow copying OPPs tables between devices
  2019-06-08  4:43 [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Saravana Kannan
                   ` (6 preceding siblings ...)
  2019-06-08  4:43 ` [PATCH v1 7/9] OPP: Add function to look up required OPP's for a given OPP Saravana Kannan
@ 2019-06-08  4:43 ` Saravana Kannan
  2019-06-08  4:43 ` [PATCH v1 9/9] interconnect: Add devfreq support Saravana Kannan
  2019-06-08  5:47 ` [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Bjorn Andersson
  9 siblings, 0 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-08  4:43 UTC (permalink / raw)
  To: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
  Cc: Saravana Kannan, Rajendra Nayak, Jordan Crouse, vincent.guittot,
	bjorn.andersson, amit.kucheria, seansw, daidavid1, evgreen,
	sibis, kernel-team, linux-pm, devicetree, linux-kernel

Some hardware devices might create multiple children devices to manage
different components of the hardware. In these cases, it might be necessary
for the original hardware device to copy specific OPP tables to a specific
the new child device. Add dev_pm_opp_add_opp_table() to do that.

Signed-off-by: Saravana Kannan <saravanak@google.com>
---
 drivers/opp/core.c     | 8 ++++++++
 include/linux/pm_opp.h | 7 +++++++
 2 files changed, 15 insertions(+)

diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index d96d5746eb47..9d49aee2c3ef 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -943,6 +943,14 @@ struct opp_device *_add_opp_dev(const struct device *dev,
 	return opp_dev;
 }
 
+int dev_pm_opp_add_opp_table(struct device *dev, struct opp_table *opp_table)
+{
+	if (!dev || !opp_table)
+		return -EINVAL;
+
+	return _add_opp_dev(dev, opp_table) ? 0 : -ENOMEM;
+}
+
 static struct opp_table *_allocate_opp_table(struct device *dev, int index)
 {
 	struct opp_table *opp_table;
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 13381dc3dd39..31acc617fda3 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -81,6 +81,7 @@ struct dev_pm_set_opp_data {
 struct opp_table *dev_pm_opp_get_opp_table(struct device *dev);
 struct opp_table *dev_pm_opp_get_opp_table_indexed(struct device *dev, int index);
 void dev_pm_opp_put_opp_table(struct opp_table *opp_table);
+int dev_pm_opp_add_opp_table(struct device *dev, struct opp_table *opp_table);
 
 unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp);
 
@@ -158,6 +159,12 @@ static inline struct opp_table *dev_pm_opp_get_opp_table_indexed(struct device *
 
 static inline void dev_pm_opp_put_opp_table(struct opp_table *opp_table) {}
 
+static int dev_pm_opp_add_opp_table(struct device *dev,
+				    struct opp_table *opp_table)
+{
+	return -ENOTSUPP;
+}
+
 static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
 {
 	return 0;
-- 
2.22.0.rc2.383.gf4fbbf30c2-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v1 9/9] interconnect: Add devfreq support
  2019-06-08  4:43 [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Saravana Kannan
                   ` (7 preceding siblings ...)
  2019-06-08  4:43 ` [PATCH v1 8/9] OPP: Allow copying OPPs tables between devices Saravana Kannan
@ 2019-06-08  4:43 ` Saravana Kannan
  2019-06-08  5:47 ` [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Bjorn Andersson
  9 siblings, 0 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-08  4:43 UTC (permalink / raw)
  To: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
  Cc: Saravana Kannan, Rajendra Nayak, Jordan Crouse, vincent.guittot,
	bjorn.andersson, amit.kucheria, seansw, daidavid1, evgreen,
	sibis, kernel-team, linux-pm, devicetree, linux-kernel

Add a icc_create_devfreq() and icc_remove_devfreq() to create and remove
devfreq devices for interconnect paths. A driver can create/remove devfreq
devices for the interconnects needed for its device by calling these APIs.
This would allow various devfreq governors to work with interconnect paths
and the device driver itself doesn't have to actively manage the bandwidth
votes for the interconnects.

Signed-off-by: Saravana Kannan <saravanak@google.com>
---
 drivers/interconnect/Makefile      |   2 +-
 drivers/interconnect/icc-devfreq.c | 145 +++++++++++++++++++++++++++++
 include/linux/interconnect.h       |  11 +++
 3 files changed, 157 insertions(+), 1 deletion(-)
 create mode 100644 drivers/interconnect/icc-devfreq.c

diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
index 28f2ab0824d5..ddfb65b7fa55 100644
--- a/drivers/interconnect/Makefile
+++ b/drivers/interconnect/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 
-icc-core-objs				:= core.o
+icc-core-objs				:= core.o icc-devfreq.o
 
 obj-$(CONFIG_INTERCONNECT)		+= icc-core.o
 obj-$(CONFIG_INTERCONNECT_QCOM)		+= qcom/
diff --git a/drivers/interconnect/icc-devfreq.c b/drivers/interconnect/icc-devfreq.c
new file mode 100644
index 000000000000..f88b267d281e
--- /dev/null
+++ b/drivers/interconnect/icc-devfreq.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * A devfreq device driver for interconnect paths
+ *
+ * Copyright (C) 2019 Google, Inc
+ */
+
+#include <linux/devfreq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/interconnect.h>
+#include <linux/limits.h>
+
+struct icc_devfreq {
+	struct device icc_dev;
+	struct icc_path *path;
+	struct devfreq_dev_profile dp;
+	struct devfreq *df;
+	unsigned long peak_bw;
+	unsigned long avg_bw;
+};
+
+static int icc_target(struct device *dev, unsigned long *freq,
+				u32 flags)
+{
+	struct icc_devfreq *d = dev_get_drvdata(dev);
+	struct dev_pm_opp *opp;
+	unsigned long peak_bw, avg_bw;
+
+	opp = devfreq_recommended_opp(dev, &peak_bw, flags);
+	if (IS_ERR(opp)) {
+		dev_err(dev, "Failed to find opp for %lu KHz\n", *freq);
+		return PTR_ERR(opp);
+	}
+	peak_bw = dev_pm_opp_get_bw(opp, &avg_bw);
+	dev_pm_opp_put(opp);
+
+	if (!icc_set_bw(d->path, avg_bw, peak_bw)) {
+		*freq = peak_bw;
+		d->peak_bw = peak_bw;
+		d->avg_bw = avg_bw;
+	}
+
+	return 0;
+}
+
+static int icc_get_dev_status(struct device *dev,
+					struct devfreq_dev_status *stat)
+{
+	struct icc_devfreq *d = dev_get_drvdata(dev);
+
+	stat->current_frequency = d->peak_bw;
+	return 0;
+}
+
+#define icc_dev_to_data(DEV)	container_of((DEV), struct icc_devfreq, icc_dev)
+static void icc_dev_release(struct device *dev)
+{
+	struct icc_devfreq *d = icc_dev_to_data(dev);
+
+	kfree(d);
+}
+
+struct icc_devfreq *icc_create_devfreq(struct device *dev, char *name)
+{
+	struct icc_devfreq *d;
+	struct icc_path *path;
+	struct opp_table *opp_table;
+	struct dev_pm_opp *opp;
+	unsigned long peak_bw = U32_MAX, avg_bw = U32_MAX;
+	int err;
+
+	if (!name)
+		return ERR_PTR(-EINVAL);
+
+	path = of_icc_get(dev, name);
+	if (IS_ERR(path))
+		return (void *) path;
+
+	opp_table = icc_get_opp_table(path);
+	if (!opp_table) {
+		err = -EINVAL;
+		goto out_path;
+	}
+
+	d = kzalloc(sizeof(*d), GFP_KERNEL);
+	if (!d) {
+		err = -ENOMEM;
+		goto out_path;
+	}
+	d->path = path;
+
+	d->icc_dev.parent = dev;
+	d->icc_dev.release = icc_dev_release;
+	dev_set_name(&d->icc_dev, "%s-icc-%s", dev_name(dev), name);
+	err = device_register(&d->icc_dev);
+	if (err) {
+		put_device(&d->icc_dev);
+		goto out_path;
+	}
+
+	dev_pm_opp_add_opp_table(&d->icc_dev, opp_table);
+	opp = dev_pm_opp_find_peak_bw_floor(dev, &peak_bw);
+	peak_bw = dev_pm_opp_get_bw(opp, &avg_bw);
+	dev_pm_opp_put(opp);
+
+	if (!icc_set_bw(d->path, avg_bw, peak_bw)) {
+		d->peak_bw = peak_bw;
+		d->avg_bw = avg_bw;
+	}
+
+	d->dp.initial_freq = peak_bw;
+	d->dp.polling_ms = 0;
+	d->dp.target = icc_target;
+	d->dp.get_dev_status = icc_get_dev_status;
+	d->df = devm_devfreq_add_device(&d->icc_dev,
+					&d->dp,
+					"performance",
+					NULL);
+	if (IS_ERR(d->df)) {
+		err = PTR_ERR(d->df);
+		goto out_dev;
+	}
+
+	return d;
+out_dev:
+	put_device(&d->icc_dev);
+out_path:
+	icc_put(path);
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(icc_create_devfreq);
+
+void icc_remove_devfreq(struct icc_devfreq *d)
+{
+	icc_put(d->path);
+	put_device(&d->icc_dev);
+}
+EXPORT_SYMBOL_GPL(icc_remove_devfreq);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("interconnect devfreq device driver");
+MODULE_AUTHOR("Saravana Kannan <saravanak@google.com>");
diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h
index 0c0bc55f0e89..cb166cbcdf88 100644
--- a/include/linux/interconnect.h
+++ b/include/linux/interconnect.h
@@ -23,6 +23,7 @@
 
 struct icc_path;
 struct device;
+struct icc_devfreq;
 
 #if IS_ENABLED(CONFIG_INTERCONNECT)
 
@@ -32,6 +33,8 @@ struct icc_path *of_icc_get(struct device *dev, const char *name);
 struct opp_table *icc_get_opp_table(struct icc_path *path);
 void icc_put(struct icc_path *path);
 int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw);
+struct icc_devfreq *icc_create_devfreq(struct device *dev, char *name);
+void icc_remove_devfreq(struct icc_devfreq *d);
 
 #else
 
@@ -61,6 +64,14 @@ static inline int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
 	return 0;
 }
 
+static inline struct icc_devfreq *icc_create_devfreq(struct device *dev,
+						     char *name)
+{
+	return NULL;
+}
+
+void icc_remove_devfreq(struct icc_devfreq *d) {}
+
 #endif /* CONFIG_INTERCONNECT */
 
 #endif /* __LINUX_INTERCONNECT_H */
-- 
2.22.0.rc2.383.gf4fbbf30c2-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver
  2019-06-08  4:43 [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Saravana Kannan
                   ` (8 preceding siblings ...)
  2019-06-08  4:43 ` [PATCH v1 9/9] interconnect: Add devfreq support Saravana Kannan
@ 2019-06-08  5:47 ` Bjorn Andersson
  2019-06-08 20:34   ` Saravana Kannan
  2019-06-10 23:12   ` [Example code] crypto: qcom-rng: Add interconnect devfreq support Saravana Kannan
  9 siblings, 2 replies; 13+ messages in thread
From: Bjorn Andersson @ 2019-06-08  5:47 UTC (permalink / raw)
  To: Saravana Kannan
  Cc: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki, Rajendra Nayak,
	Jordan Crouse, vincent.guittot, amit.kucheria, seansw, daidavid1,
	evgreen, sibis, kernel-team, linux-pm, devicetree, linux-kernel

On Fri 07 Jun 21:43 PDT 2019, Saravana Kannan wrote:

> I replied[1] to this patch series[2] and described how I think interconnect
> bandwidth voting should be captured in DT and how it should work.
> 
> So sending out a patch series implementing that. This patch series does the
> following:
> - Adds Bandwidth OPP table support (this adds device freq to bandwidth
>   mapping for free)
> - Adds a devfreq library for interconnect paths
> 

Please provide a driver that uses this devfreq library, without it this
its impossible to gauge the usefulness of your approach.

> Interconnects and interconnect paths quantify they performance levels in
> terms of bandwidth. So similar to how we have frequency based OPP tables
> in DT and in the OPP framework, this patch series adds bandwidth OPP
> table support in the OPP framework and in DT.
> 
> To simplify voting for interconnects, this patch series adds helper
> functions to create devfreq devices out of interconnect paths. This
> allows drivers to add a single line of code to add interconnect voting
> capability.
> 
> To add devfreq device for the "gpu-mem" interconnect path:
> icc_create_devfreq(dev, "gpu-mem");
> 
> With the future addition of a "passive_bandwidth" devfreq governor,
> device frequency to interconnect bandwidth mapping would come for free.
> 
> If the feedback on this patch series is positive, I'll then add the
> devfreq passive_bandwidth governor (or something similar) to v2 of this
> patch series.
> 
> So with the DT bindings added in this patch series, the DT for a GPU
> that does bandwidth voting from GPU to Cache and GPU to DDR would look
> something like this:
> 
> gpu_cache_opp_table: gpu_cache_opp_table {
> 	compatible = "operating-points-v2";
> 
> 	gpu_cache_3000: opp-3000 {
> 		opp-peak-KBps = <3000>;
> 		opp-avg-KBps = <1000>;
> 	};
> 	gpu_cache_6000: opp-6000 {
> 		opp-peak-KBps = <6000>;
> 		opp-avg-KBps = <2000>;
> 	};
> 	gpu_cache_9000: opp-9000 {
> 		opp-peak-KBps = <9000>;
> 		opp-avg-KBps = <9000>;
> 	};
> };
> 
> gpu_ddr_opp_table: gpu_ddr_opp_table {
> 	compatible = "operating-points-v2";
> 
> 	gpu_ddr_1525: opp-1525 {
> 		opp-peak-KBps = <1525>;
> 		opp-avg-KBps = <452>;
> 	};
> 	gpu_ddr_3051: opp-3051 {
> 		opp-peak-KBps = <3051>;
> 		opp-avg-KBps = <915>;
> 	};
> 	gpu_ddr_7500: opp-7500 {
> 		opp-peak-KBps = <7500>;
> 		opp-avg-KBps = <3000>;
> 	};
> };
> 
> gpu_opp_table: gpu_opp_table {
> 	compatible = "operating-points-v2";
> 	opp-shared;
> 
> 	opp-200000000 {
> 		opp-hz = /bits/ 64 <200000000>;
> 		required-opps = <&gpu_cache_3000>, <&gpu_ddr_1525>;

I still don't see the benefit of the indirection, over just spelling out
the bandwidth values here.

Regards,
Bjorn

> 	};
> 	opp-400000000 {
> 		opp-hz = /bits/ 64 <400000000>;
> 		required-opps = <&gpu_cache_6000>, <&gpu_ddr_3051>;
> 	};
> };
> 
> gpu@7864000 {
> 	...
> 	operating-points-v2 = <&gpu_opp_table>, <&gpu_cache_opp_table>, <&gpu_ddr_opp_table>;
> 	interconnects = <&mmnoc MASTER_GPU_1 &bimc SLAVE_SYSTEL_CACHE>,
> 			<&mmnoc MASTER_GPU_1 &bimc SLAVE_DDR>;
> 	interconnect-names = "gpu-cache", "gpu-mem";
> 	interconnect-opp-table = <&gpu_cache_opp_table>, <&gpu_ddr_opp_table>
> };
> 
> Cheers,
> Saravana
> 
> [1] - https://lore.kernel.org/lkml/20190601021228.210574-1-saravanak@google.com/
> [2] - https://lore.kernel.org/lkml/20190423132823.7915-1-georgi.djakov@linaro.org/ 
> 
> Saravana Kannan (9):
>   dt-bindings: opp: Introduce opp-peak-KBps and opp-avg-KBps bindings
>   OPP: Add support for bandwidth OPP tables
>   OPP: Add helper function for bandwidth OPP tables
>   OPP: Add API to find an OPP table from its DT node
>   dt-bindings: interconnect: Add interconnect-opp-table property
>   interconnect: Add OPP table support for interconnects
>   OPP: Add function to look up required OPP's for a given OPP
>   OPP: Allow copying OPPs tables between devices
>   interconnect: Add devfreq support
> 
>  .../bindings/interconnect/interconnect.txt    |   8 +
>  Documentation/devicetree/bindings/opp/opp.txt |  15 +-
>  drivers/interconnect/Makefile                 |   2 +-
>  drivers/interconnect/core.c                   |  27 +++-
>  drivers/interconnect/icc-devfreq.c            | 144 ++++++++++++++++++
>  drivers/opp/core.c                            | 109 +++++++++++++
>  drivers/opp/of.c                              |  75 +++++++--
>  drivers/opp/opp.h                             |   4 +-
>  include/linux/interconnect.h                  |  17 +++
>  include/linux/pm_opp.h                        |  41 +++++
>  10 files changed, 426 insertions(+), 16 deletions(-)
>  create mode 100644 drivers/interconnect/icc-devfreq.c
> 
> -- 
> 2.22.0.rc2.383.gf4fbbf30c2-goog
> 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver
  2019-06-08  5:47 ` [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Bjorn Andersson
@ 2019-06-08 20:34   ` Saravana Kannan
  2019-06-10 23:12   ` [Example code] crypto: qcom-rng: Add interconnect devfreq support Saravana Kannan
  1 sibling, 0 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-08 20:34 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki, Rajendra Nayak,
	Jordan Crouse, Vincent Guittot, amit.kucheria, seansw, daidavid1,
	evgreen, sibis, Android Kernel Team, linux-pm, devicetree,
	linux-kernel

On Fri, Jun 7, 2019 at 10:46 PM Bjorn Andersson
<bjorn.andersson@linaro.org> wrote:
>
> On Fri 07 Jun 21:43 PDT 2019, Saravana Kannan wrote:
>
> > I replied[1] to this patch series[2] and described how I think interconnect
> > bandwidth voting should be captured in DT and how it should work.
> >
> > So sending out a patch series implementing that. This patch series does the
> > following:
> > - Adds Bandwidth OPP table support (this adds device freq to bandwidth
> >   mapping for free)
> > - Adds a devfreq library for interconnect paths
> >
>
> Please provide a driver that uses this devfreq library, without it this
> its impossible to gauge the usefulness of your approach.

Do you have a device/driver from SDM845 that I could add interconnect
support to other than CPUs? Because the CPU driver doesn't even have
an OPP table (because it gets that from hardware) and I don't want to
deal with that right now. Can you also give me the "interconnect"
values for those so that I can have something valid that you can try
out?

> > Interconnects and interconnect paths quantify they performance levels in
> > terms of bandwidth. So similar to how we have frequency based OPP tables
> > in DT and in the OPP framework, this patch series adds bandwidth OPP
> > table support in the OPP framework and in DT.
> >
> > To simplify voting for interconnects, this patch series adds helper
> > functions to create devfreq devices out of interconnect paths. This
> > allows drivers to add a single line of code to add interconnect voting
> > capability.
> >
> > To add devfreq device for the "gpu-mem" interconnect path:
> > icc_create_devfreq(dev, "gpu-mem");
> >
> > With the future addition of a "passive_bandwidth" devfreq governor,
> > device frequency to interconnect bandwidth mapping would come for free.
> >
> > If the feedback on this patch series is positive, I'll then add the
> > devfreq passive_bandwidth governor (or something similar) to v2 of this
> > patch series.
> >
> > So with the DT bindings added in this patch series, the DT for a GPU
> > that does bandwidth voting from GPU to Cache and GPU to DDR would look
> > something like this:
> >
> > gpu_cache_opp_table: gpu_cache_opp_table {
> >       compatible = "operating-points-v2";
> >
> >       gpu_cache_3000: opp-3000 {
> >               opp-peak-KBps = <3000>;
> >               opp-avg-KBps = <1000>;
> >       };
> >       gpu_cache_6000: opp-6000 {
> >               opp-peak-KBps = <6000>;
> >               opp-avg-KBps = <2000>;
> >       };
> >       gpu_cache_9000: opp-9000 {
> >               opp-peak-KBps = <9000>;
> >               opp-avg-KBps = <9000>;
> >       };
> > };
> >
> > gpu_ddr_opp_table: gpu_ddr_opp_table {
> >       compatible = "operating-points-v2";
> >
> >       gpu_ddr_1525: opp-1525 {
> >               opp-peak-KBps = <1525>;
> >               opp-avg-KBps = <452>;
> >       };
> >       gpu_ddr_3051: opp-3051 {
> >               opp-peak-KBps = <3051>;
> >               opp-avg-KBps = <915>;
> >       };
> >       gpu_ddr_7500: opp-7500 {
> >               opp-peak-KBps = <7500>;
> >               opp-avg-KBps = <3000>;
> >       };
> > };
> >
> > gpu_opp_table: gpu_opp_table {
> >       compatible = "operating-points-v2";
> >       opp-shared;
> >
> >       opp-200000000 {
> >               opp-hz = /bits/ 64 <200000000>;
> >               required-opps = <&gpu_cache_3000>, <&gpu_ddr_1525>;
>
> I still don't see the benefit of the indirection, over just spelling out
> the bandwidth values here.

All the things I mentioned in the other email? This explicitly
enumerates the actual BW OPPs in the interconnect paths as opposed to
getting a partial list of "performance points" obtained from the
mapping. Put another way, it's like how you don't try to enumerate the
voltages supported by a regualtor by looking at the mappings in the
OPP of a device using the regulator. You instead capture the regulator
properties separately and the consumer asks what it wants and then
that's bound within the scope of what the regulator can support.

Also, if more properties are added in the future to interconnects
(priority, active vs sleep or tagging, etc) the BW OPP tables is well
suited to capture them than adding them to the GPU/CPU/device's
mapping to what it wants from the interconnect.

This method also removes the ambiguity of which bandwidth mapping
belongs to which interconnect path (the question Rajendra was asking
in the other thread) and the peak vs avg bandwidth.

Also with the interconnect paths actually enumerating the OPPs, you
don't have to have a useless mapping just so that you bandwidth
counter based scheme can get a list of the available OPPs.

Also, it is not unusual for the device OPP count vs the interconnect
path OPP count to be different. So, if their counts aren't the same,
the method used in the other thread can't capture the OPP lists
fully/properly.

-Saravana

>
> Regards,
> Bjorn
>
> >       };
> >       opp-400000000 {
> >               opp-hz = /bits/ 64 <400000000>;
> >               required-opps = <&gpu_cache_6000>, <&gpu_ddr_3051>;
> >       };
> > };
> >
> > gpu@7864000 {
> >       ...
> >       operating-points-v2 = <&gpu_opp_table>, <&gpu_cache_opp_table>, <&gpu_ddr_opp_table>;
> >       interconnects = <&mmnoc MASTER_GPU_1 &bimc SLAVE_SYSTEL_CACHE>,
> >                       <&mmnoc MASTER_GPU_1 &bimc SLAVE_DDR>;
> >       interconnect-names = "gpu-cache", "gpu-mem";
> >       interconnect-opp-table = <&gpu_cache_opp_table>, <&gpu_ddr_opp_table>
> > };
> >
> > Cheers,
> > Saravana
> >
> > [1] - https://lore.kernel.org/lkml/20190601021228.210574-1-saravanak@google.com/
> > [2] - https://lore.kernel.org/lkml/20190423132823.7915-1-georgi.djakov@linaro.org/
> >
> > Saravana Kannan (9):
> >   dt-bindings: opp: Introduce opp-peak-KBps and opp-avg-KBps bindings
> >   OPP: Add support for bandwidth OPP tables
> >   OPP: Add helper function for bandwidth OPP tables
> >   OPP: Add API to find an OPP table from its DT node
> >   dt-bindings: interconnect: Add interconnect-opp-table property
> >   interconnect: Add OPP table support for interconnects
> >   OPP: Add function to look up required OPP's for a given OPP
> >   OPP: Allow copying OPPs tables between devices
> >   interconnect: Add devfreq support
> >
> >  .../bindings/interconnect/interconnect.txt    |   8 +
> >  Documentation/devicetree/bindings/opp/opp.txt |  15 +-
> >  drivers/interconnect/Makefile                 |   2 +-
> >  drivers/interconnect/core.c                   |  27 +++-
> >  drivers/interconnect/icc-devfreq.c            | 144 ++++++++++++++++++
> >  drivers/opp/core.c                            | 109 +++++++++++++
> >  drivers/opp/of.c                              |  75 +++++++--
> >  drivers/opp/opp.h                             |   4 +-
> >  include/linux/interconnect.h                  |  17 +++
> >  include/linux/pm_opp.h                        |  41 +++++
> >  10 files changed, 426 insertions(+), 16 deletions(-)
> >  create mode 100644 drivers/interconnect/icc-devfreq.c
> >
> > --
> > 2.22.0.rc2.383.gf4fbbf30c2-goog
> >

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [Example code] crypto: qcom-rng: Add interconnect devfreq support
  2019-06-08  5:47 ` [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Bjorn Andersson
  2019-06-08 20:34   ` Saravana Kannan
@ 2019-06-10 23:12   ` Saravana Kannan
  1 sibling, 0 replies; 13+ messages in thread
From: Saravana Kannan @ 2019-06-10 23:12 UTC (permalink / raw)
  To: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki,
	0001-crypto-qcom-rng-Add-interconnect-dev.patch
  Cc: Saravana Kannan, Rajendra Nayak, Jordan Crouse, vincent.guittot,
	bjorn.andersson, amit.kucheria, seansw, daidavid1, evgreen,
	sibis, kernel-team, linux-pm, devicetree, linux-kernel

This is just an example code and I'm not actually trying to get this
merged.

Bjorn,

This is what the code would look like. It compiles, but I can't test it.
I'm not sending this as part of the patch series as I'm not sure what
the interconnects property should be set to in DT for qcom-rng device
nor is QCOM RNG an ideal candidate for bandwidth scaling using devfreq.
It can probably just set the bandwidth to some fixed low value like 1
KB/s. But this is pretty much all the code one would need if they wanted
to enable interconnect scaling for their interconnect path using
devfreq.

-Saravana

Signed-off-by: Saravana Kannan <saravanak@google.com>
---
 drivers/crypto/qcom-rng.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/crypto/qcom-rng.c b/drivers/crypto/qcom-rng.c
index e54249ccc009..7a92238061ca 100644
--- a/drivers/crypto/qcom-rng.c
+++ b/drivers/crypto/qcom-rng.c
@@ -9,6 +9,7 @@
 #include <linux/crypto.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/interconnect.h>
 #include <linux/platform_device.h>
 
 /* Device specific register offsets */
@@ -37,6 +38,7 @@ struct qcom_rng_ctx {
 };
 
 static struct qcom_rng *qcom_rng_dev;
+static struct icc_devfreq *icc_df;
 
 static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max)
 {
@@ -186,6 +188,13 @@ static int qcom_rng_probe(struct platform_device *pdev)
 		qcom_rng_dev = NULL;
 	}
 
+	ret = dev_pm_opp_of_add_table_indexed(&pdev->dev, 0);
+	if (ret)
+		return ret;
+	icc_df = icc_create_devfreq(&pdev->dev, "prng-slv");
+	if (IS_ERR(icc_df))
+		return PTR_ERR(icc_df);
+
 	return ret;
 }
 
-- 
2.22.0.rc2.383.gf4fbbf30c2-goog


^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2019-06-10 23:12 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-08  4:43 [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Saravana Kannan
2019-06-08  4:43 ` [PATCH v1 1/9] dt-bindings: opp: Introduce opp-peak-KBps and opp-avg-KBps bindings Saravana Kannan
2019-06-08  4:43 ` [PATCH v1 2/9] OPP: Add support for bandwidth OPP tables Saravana Kannan
2019-06-08  4:43 ` [PATCH v1 3/9] OPP: Add helper function " Saravana Kannan
2019-06-08  4:43 ` [PATCH v1 4/9] OPP: Add API to find an OPP table from its DT node Saravana Kannan
2019-06-08  4:43 ` [PATCH v1 5/9] dt-bindings: interconnect: Add interconnect-opp-table property Saravana Kannan
2019-06-08  4:43 ` [PATCH v1 6/9] interconnect: Add OPP table support for interconnects Saravana Kannan
2019-06-08  4:43 ` [PATCH v1 7/9] OPP: Add function to look up required OPP's for a given OPP Saravana Kannan
2019-06-08  4:43 ` [PATCH v1 8/9] OPP: Allow copying OPPs tables between devices Saravana Kannan
2019-06-08  4:43 ` [PATCH v1 9/9] interconnect: Add devfreq support Saravana Kannan
2019-06-08  5:47 ` [PATCH v1 0/9] Introduce Bandwidth OPPs & interconnect devfreq driver Bjorn Andersson
2019-06-08 20:34   ` Saravana Kannan
2019-06-10 23:12   ` [Example code] crypto: qcom-rng: Add interconnect devfreq support Saravana Kannan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).