This patch allows the OPP core to parse the "domain-performance-state"
property in the OPP nodes. The nodes are allowed to have the
"domain-performance-state" property, only if the device node contains a
"power-domains" property. The OPP nodes aren't allowed to contain the
property partially, i.e. Either all OPP nodes in the OPP table have the
"domain-performance-state" property or none of them have it.

Signed-off-by: Viresh Kumar <viresh.ku...@linaro.org>
Tested-by: Rajendra Nayak <rna...@codeaurora.org>
---
 drivers/base/power/opp/core.c    | 73 ++++++++++++++++++++++++++++++++++++++++
 drivers/base/power/opp/debugfs.c |  4 +++
 drivers/base/power/opp/of.c      | 37 ++++++++++++++++++++
 drivers/base/power/opp/opp.h     | 12 +++++++
 4 files changed, 126 insertions(+)

diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index 91ec3232d630..211551f377e9 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -542,6 +542,63 @@ _generic_set_opp_clk_only(struct device *dev, struct clk 
*clk,
        return ret;
 }
 
+static int _update_pm_qos_request(struct device *dev,
+                                 struct dev_pm_qos_request *req,
+                                 unsigned int perf)
+{
+       int ret;
+
+       if (likely(dev_pm_qos_request_active(req)))
+               ret = dev_pm_qos_update_request(req, perf);
+       else
+               ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_PERFORMANCE,
+                                            perf);
+
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int _generic_set_opp_pd(struct device *dev, struct clk *clk,
+                              struct dev_pm_qos_request *req,
+                              unsigned long old_freq, unsigned long freq,
+                              unsigned int old_perf, unsigned int new_perf)
+{
+       int ret;
+
+       /* Scaling up? Scale voltage before frequency */
+       if (freq > old_freq) {
+               ret = _update_pm_qos_request(dev, req, new_perf);
+               if (ret)
+                       return ret;
+       }
+
+       /* Change frequency */
+       ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
+       if (ret)
+               goto restore_perf;
+
+       /* Scaling down? Scale voltage after frequency */
+       if (freq < old_freq) {
+               ret = _update_pm_qos_request(dev, req, new_perf);
+               if (ret)
+                       goto restore_freq;
+       }
+
+       return 0;
+
+restore_freq:
+       if (_generic_set_opp_clk_only(dev, clk, freq, old_freq))
+               dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
+                       __func__, old_freq);
+restore_perf:
+       if (old_perf)
+               _update_pm_qos_request(dev, req, old_perf);
+
+       return ret;
+}
+
 static int _generic_set_opp(struct dev_pm_set_opp_data *data)
 {
        struct dev_pm_opp_supply *old_supply = data->old_opp.supplies;
@@ -662,6 +719,19 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long 
target_freq)
 
        regulators = opp_table->regulators;
 
+       /* Has power domains performance states */
+       if (opp_table->has_pd_perf_states) {
+               unsigned int old_perf = 0, new_perf;
+               struct dev_pm_qos_request *req = &opp_table->qos_request;
+
+               new_perf = opp->pd_perf_state;
+               if (!IS_ERR(old_opp))
+                       old_perf = old_opp->pd_perf_state;
+
+               return _generic_set_opp_pd(dev, clk, req, old_freq, freq,
+                                          old_perf, new_perf);
+       }
+
        /* Only frequency scaling */
        if (!regulators) {
                ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
@@ -807,6 +877,9 @@ static void _opp_table_kref_release(struct kref *kref)
        struct opp_table *opp_table = container_of(kref, struct opp_table, 
kref);
        struct opp_device *opp_dev;
 
+       if (dev_pm_qos_request_active(&opp_table->qos_request))
+               dev_pm_qos_remove_request(&opp_table->qos_request);
+
        /* Release clk */
        if (!IS_ERR(opp_table->clk))
                clk_put(opp_table->clk);
diff --git a/drivers/base/power/opp/debugfs.c b/drivers/base/power/opp/debugfs.c
index 95f433db4ac7..264958ab3de9 100644
--- a/drivers/base/power/opp/debugfs.c
+++ b/drivers/base/power/opp/debugfs.c
@@ -104,6 +104,10 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct 
opp_table *opp_table)
        if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate))
                return -ENOMEM;
 
+       if (!debugfs_create_u32("power_domain_perf_state", S_IRUGO, d,
+                               &opp->pd_perf_state))
+               return -ENOMEM;
+
        if (!opp_debug_create_supplies(opp, opp_table, d))
                return -ENOMEM;
 
diff --git a/drivers/base/power/opp/of.c b/drivers/base/power/opp/of.c
index 779428676f63..e3b5f10e7f25 100644
--- a/drivers/base/power/opp/of.c
+++ b/drivers/base/power/opp/of.c
@@ -311,6 +311,38 @@ static int _opp_add_static_v2(struct opp_table *opp_table, 
struct device *dev,
        if (!of_property_read_u32(np, "clock-latency-ns", &val))
                new_opp->clock_latency_ns = val;
 
+       /*
+        * Make sure that all information is present around domain power states
+        * and nothing is left out.
+        */
+       if (!of_property_read_u32(np, "domain-performance-state",
+                                 &new_opp->pd_perf_state)) {
+               if (!opp_table->has_pd) {
+                       ret = -EINVAL;
+                       dev_err(dev, "%s: OPP node can't have performance state 
as device doesn't have power-domain\n",
+                               __func__);
+                       goto free_opp;
+               }
+
+               if (opp_table->has_pd_perf_states == -1) {
+                       opp_table->has_pd_perf_states = 1;
+               } else if (!opp_table->has_pd_perf_states) {
+                       ret = -EINVAL;
+                       dev_err(dev, "%s: Not all OPP nodes have performance 
state\n",
+                               __func__);
+                       goto free_opp;
+               }
+       } else {
+               if (opp_table->has_pd_perf_states == -1) {
+                       opp_table->has_pd_perf_states = 0;
+               } else if (opp_table->has_pd_perf_states) {
+                       ret = -EINVAL;
+                       dev_err(dev, "%s: Not all OPP nodes have performance 
state\n",
+                               __func__);
+                       goto free_opp;
+               }
+       }
+
        ret = opp_parse_supplies(new_opp, dev, opp_table);
        if (ret)
                goto free_opp;
@@ -375,6 +407,11 @@ static int _of_add_opp_table_v2(struct device *dev, struct 
device_node *opp_np)
        if (!opp_table)
                return -ENOMEM;
 
+       if (of_find_property(dev->of_node, "power-domains", NULL)) {
+               opp_table->has_pd = true;
+               opp_table->has_pd_perf_states = -1;
+       }
+
        /* We have opp-table node now, iterate over it and add OPPs */
        for_each_available_child_of_node(opp_np, np) {
                count++;
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h
index 166eef990599..41a2c0a67031 100644
--- a/drivers/base/power/opp/opp.h
+++ b/drivers/base/power/opp/opp.h
@@ -20,6 +20,7 @@
 #include <linux/list.h>
 #include <linux/limits.h>
 #include <linux/pm_opp.h>
+#include <linux/pm_qos.h>
 #include <linux/notifier.h>
 
 struct clk;
@@ -58,6 +59,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
+ * @pd_perf_state: Performance state of power domain
  * @rate:      Frequency in hertz
  * @supplies:  Power supplies voltage/current values
  * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
@@ -76,6 +78,7 @@ struct dev_pm_opp {
        bool dynamic;
        bool turbo;
        bool suspend;
+       unsigned int pd_perf_state;
        unsigned long rate;
 
        struct dev_pm_opp_supply *supplies;
@@ -137,6 +140,11 @@ enum opp_table_access {
  * @regulator_count: Number of power supply regulators
  * @set_opp: Platform specific set_opp callback
  * @set_opp_data: Data to be passed to set_opp callback
+ * @has_pd: True if the device node contains power-domain property
+ * @has_pd_perf_states: Can have value of 0, 1 or -1. -1 means uninitialized
+ * state, 0 means that OPP nodes don't have perf states and 1 means that OPP
+ * nodes have perf states.
+ * @qos_request: Qos request.
  * @dentry:    debugfs dentry pointer of the real device directory (not links).
  * @dentry_name: Name of the real dentry.
  *
@@ -174,6 +182,10 @@ struct opp_table {
        int (*set_opp)(struct dev_pm_set_opp_data *data);
        struct dev_pm_set_opp_data *set_opp_data;
 
+       bool has_pd;
+       int has_pd_perf_states;
+       struct dev_pm_qos_request qos_request;
+
 #ifdef CONFIG_DEBUG_FS
        struct dentry *dentry;
        char dentry_name[NAME_MAX];
-- 
2.7.1.410.g6faf27b

Reply via email to