@@ -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);
@@ -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__ */
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(+)