[RFC,3/4] opp: Add dev_pm_parse_required_opp_as_qos
diff mbox series

Message ID 0a7339f8b35d58c2272ce9fb7c025a5628afd921.1565089196.git.leonard.crestez@nxp.com
State RFC, archived
Headers show
Series
  • opp: Parse required-opp as dev_pm_qos_request
Related show

Commit Message

Leonard Crestez Aug. 6, 2019, 11:12 a.m. UTC
The "required-opps" property can be placed on any device and point to
any OPP table according to bindings doc but this is not fully
implemented. In practice it can only point from the opp table of a
device to the opp table of a power domain.

Add support for parsing "required-opps" as a DEV_PM_QOS_MIN_FREQUENCY
request.

Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
---
 drivers/opp/of.c       | 65 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_opp.h |  9 ++++++
 2 files changed, 74 insertions(+)

Patch
diff mbox series

diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index 1813f5ad5fa2..a086bb120fec 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -16,10 +16,11 @@ 
 #include <linux/of_device.h>
 #include <linux/pm_domain.h>
 #include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/energy_model.h>
+#include <linux/pm_qos.h>
 
 #include "opp.h"
 
 /*
  * Returns opp descriptor node for a device node, caller must
@@ -1119,5 +1120,69 @@  void dev_pm_opp_of_register_em(struct cpumask *cpus)
 		return;
 
 	em_register_perf_domain(cpus, nr_opp, &em_cb);
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_of_register_em);
+
+int dev_pm_parse_required_opp_as_qos(struct device_node *node,
+				     struct dev_pm_qos_request *req)
+{
+	struct device_node *opp_node;
+	struct opp_table *opp_table;
+	struct dev_pm_opp *opp;
+	struct device *req_dev;
+	unsigned long req_val;
+	int ret;
+
+	mutex_lock(&opp_table_lock);
+
+	opp_node = of_parse_required_opp(node, 0);
+	if (!opp_node) {
+		ret = -ENOENT;
+		goto unlock_opp_tables;
+	}
+
+	opp_table = _find_table_of_opp_np(opp_node);
+	if (IS_ERR(opp_table)) {
+		ret = -EPROBE_DEFER;
+		goto put_opp_node;
+	}
+
+	opp = _find_opp_of_np(opp_table, opp_node);
+	if (!opp) {
+		ret = -ENOENT;
+		goto put_opp_table;
+	}
+
+	req_dev = dev_pm_opp_table_get_device(opp_table);
+	if (IS_ERR(req_dev)) {
+		pr_err("%pOF: failed to fetch device for table %pOF\n",
+				node, opp_table->np);
+		ret = PTR_ERR(req_dev);
+		goto put_opp;
+	}
+
+	req_val = dev_pm_opp_get_freq(opp);
+	if (req_val > S32_MAX) {
+		ret = -ERANGE;
+		goto put_opp;
+	}
+
+	mutex_unlock(&opp_table_lock);
+
+	ret = dev_pm_qos_add_request(req_dev, req,
+			DEV_PM_QOS_MIN_FREQUENCY, req_val);
+	if (ret < 0)
+		pr_err("%pOF: failed to add dev_pm_qos request: %d\n", node, ret);
+	ret = 0;
+
+put_opp:
+	dev_pm_opp_put(opp);
+put_opp_table:
+	dev_pm_opp_put_opp_table(opp_table);
+put_opp_node:
+	of_node_put(opp_node);
+unlock_opp_tables:
+	mutex_unlock(&opp_table_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_parse_required_opp_as_qos);
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index a4db3f42d787..949c35e8f5ae 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -17,10 +17,11 @@ 
 struct clk;
 struct regulator;
 struct dev_pm_opp;
 struct device;
 struct opp_table;
+struct dev_pm_qos_request;
 
 enum dev_pm_opp_event {
 	OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
 };
 
@@ -352,10 +353,12 @@  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 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);
+int dev_pm_parse_required_opp_as_qos(struct device_node *node,
+				     struct dev_pm_qos_request *req);
 #else
 static inline int dev_pm_opp_of_add_table(struct device *dev)
 {
 	return -ENOTSUPP;
 }
@@ -399,8 +402,14 @@  static inline void dev_pm_opp_of_register_em(struct cpumask *cpus)
 
 static inline int of_get_required_opp_performance_state(struct device_node *np, int index)
 {
 	return -ENOTSUPP;
 }
+
+int dev_pm_parse_required_opp_as_qos(struct device_node *node,
+				     struct dev_pm_qos_request *req)
+{
+	return -ENOTSUPP;
+}
 #endif
 
 #endif		/* __LINUX_OPP_H__ */