return 0;
        }
 
-       return opp->rate;
+       return opp->rates[0];
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq);
 
 /* Helpers to read keys */
 static unsigned long _read_freq(struct dev_pm_opp *opp, int index)
 {
-       return opp->rate;
+       return opp->rates[0];
 }
 
 static unsigned long _read_level(struct dev_pm_opp *opp, int index)
        return ret;
 }
 
-static inline int _generic_set_opp_clk_only(struct device *dev,
-               struct opp_table *opp_table, struct dev_pm_opp *opp, void *data)
+static int
+_opp_config_clk_single(struct device *dev, struct opp_table *opp_table,
+                      struct dev_pm_opp *opp, void *data, bool scaling_down)
 {
        unsigned long *target = data;
        unsigned long freq;
        int ret;
 
-       /* We may reach here for devices which don't change frequency */
-       if (IS_ERR(opp_table->clk))
-               return 0;
-
        /* One of target and opp must be available */
        if (target) {
                freq = *target;
        } else if (opp) {
-               freq = opp->rate;
+               freq = opp->rates[0];
        } else {
                WARN_ON(1);
                return -EINVAL;
        }
 
        dev_dbg(dev, "%s: switching OPP: Freq %lu -> %lu Hz, Level %u -> %u, Bw %u -> %u\n",
-               __func__, old_opp->rate, opp->rate, old_opp->level, opp->level,
-               old_opp->bandwidth ? old_opp->bandwidth[0].peak : 0,
+               __func__, old_opp->rates[0], opp->rates[0], old_opp->level,
+               opp->level, old_opp->bandwidth ? old_opp->bandwidth[0].peak : 0,
                opp->bandwidth ? opp->bandwidth[0].peak : 0);
 
-       scaling_down = _opp_compare_key(old_opp, opp);
+       scaling_down = _opp_compare_key(opp_table, old_opp, opp);
        if (scaling_down == -1)
                scaling_down = 0;
 
                }
        }
 
-       ret = _generic_set_opp_clk_only(dev, opp_table, opp, clk_data);
-       if (ret)
-               return ret;
+       if (opp_table->config_clks) {
+               ret = opp_table->config_clks(dev, opp_table, opp, clk_data, scaling_down);
+               if (ret)
+                       return ret;
+       }
 
        /* Scaling down? Configure required OPPs after frequency */
        if (scaling_down) {
                 * equivalent to a clk_set_rate()
                 */
                if (!_get_opp_count(opp_table)) {
-                       ret = _generic_set_opp_clk_only(dev, opp_table, NULL,
-                                                       &target_freq);
+                       ret = opp_table->config_clks(dev, opp_table, NULL,
+                                                    &target_freq, false);
                        goto put_opp_table;
                }
 
        INIT_LIST_HEAD(&opp_table->dev_list);
        INIT_LIST_HEAD(&opp_table->lazy);
 
+       opp_table->clk = ERR_PTR(-ENODEV);
+
        /* Mark regulator count uninitialized */
        opp_table->regulator_count = -1;
 
        int ret;
 
        /*
-        * Return early if we don't need to get clk or we have already tried it
+        * Return early if we don't need to get clk or we have already done it
         * earlier.
         */
-       if (!getclk || IS_ERR(opp_table) || opp_table->clk)
+       if (!getclk || IS_ERR(opp_table) || !IS_ERR(opp_table->clk) ||
+           opp_table->clks)
                return opp_table;
 
        /* Find clk for the device */
        opp_table->clk = clk_get(dev, NULL);
 
        ret = PTR_ERR_OR_ZERO(opp_table->clk);
-       if (!ret)
+       if (!ret) {
+               opp_table->config_clks = _opp_config_clk_single;
+               opp_table->clk_count = 1;
                return opp_table;
+       }
 
        if (ret == -ENOENT) {
+               /*
+                * There are few platforms which don't want the OPP core to
+                * manage device's clock settings. In such cases neither the
+                * platform provides the clks explicitly to us, nor the DT
+                * contains a valid clk entry. The OPP nodes in DT may still
+                * contain "opp-hz" property though, which we need to parse and
+                * allow the platform to find an OPP based on freq later on.
+                *
+                * This is a simple solution to take care of such corner cases,
+                * i.e. make the clk_count 1, which lets us allocate space for
+                * frequency in opp->rates and also parse the entries in DT.
+                */
+               opp_table->clk_count = 1;
+
                dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__, ret);
                return opp_table;
        }
 
        _of_clear_opp_table(opp_table);
 
-       /* Release clk */
+       /* Release automatically acquired single clk */
        if (!IS_ERR(opp_table->clk))
                clk_put(opp_table->clk);
 
        mutex_lock(&opp_table->lock);
 
        list_for_each_entry(iter, &opp_table->opp_list, node) {
-               if (iter->rate == freq) {
+               if (iter->rates[0] == freq) {
                        opp = iter;
                        break;
                }
 struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table)
 {
        struct dev_pm_opp *opp;
-       int supply_count, supply_size, icc_size;
+       int supply_count, supply_size, icc_size, clk_size;
 
        /* Allocate space for at least one supply */
        supply_count = opp_table->regulator_count > 0 ?
                        opp_table->regulator_count : 1;
        supply_size = sizeof(*opp->supplies) * supply_count;
+       clk_size = sizeof(*opp->rates) * opp_table->clk_count;
        icc_size = sizeof(*opp->bandwidth) * opp_table->path_count;
 
        /* allocate new OPP node and supplies structures */
-       opp = kzalloc(sizeof(*opp) + supply_size + icc_size, GFP_KERNEL);
-
+       opp = kzalloc(sizeof(*opp) + supply_size + clk_size + icc_size, GFP_KERNEL);
        if (!opp)
                return NULL;
 
-       /* Put the supplies at the end of the OPP structure as an empty array */
+       /* Put the supplies, bw and clock at the end of the OPP structure */
        opp->supplies = (struct dev_pm_opp_supply *)(opp + 1);
+
+       opp->rates = (unsigned long *)(opp->supplies + supply_count);
+
        if (icc_size)
-               opp->bandwidth = (struct dev_pm_opp_icc_bw *)(opp->supplies + supply_count);
+               opp->bandwidth = (struct dev_pm_opp_icc_bw *)(opp->rates + opp_table->clk_count);
+
        INIT_LIST_HEAD(&opp->node);
 
        return opp;
        return true;
 }
 
+static int _opp_compare_rate(struct opp_table *opp_table,
+                            struct dev_pm_opp *opp1, struct dev_pm_opp *opp2)
+{
+       int i;
+
+       for (i = 0; i < opp_table->clk_count; i++) {
+               if (opp1->rates[i] != opp2->rates[i])
+                       return opp1->rates[i] < opp2->rates[i] ? -1 : 1;
+       }
+
+       /* Same rates for both OPPs */
+       return 0;
+}
+
 /*
  * Returns
  * 0: opp1 == opp2
  * 1: opp1 > opp2
  * -1: opp1 < opp2
  */
-int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2)
+int _opp_compare_key(struct opp_table *opp_table, struct dev_pm_opp *opp1,
+                    struct dev_pm_opp *opp2)
 {
-       if (opp1->rate != opp2->rate)
-               return opp1->rate < opp2->rate ? -1 : 1;
+       int ret;
+
+       ret = _opp_compare_rate(opp_table, opp1, opp2);
+       if (ret)
+               return ret;
+
        if (opp1->bandwidth && opp2->bandwidth &&
            opp1->bandwidth[0].peak != opp2->bandwidth[0].peak)
                return opp1->bandwidth[0].peak < opp2->bandwidth[0].peak ? -1 : 1;
+
        if (opp1->level != opp2->level)
                return opp1->level < opp2->level ? -1 : 1;
+
+       /* Duplicate OPPs */
        return 0;
 }
 
         * loop.
         */
        list_for_each_entry(opp, &opp_table->opp_list, node) {
-               opp_cmp = _opp_compare_key(new_opp, opp);
+               opp_cmp = _opp_compare_key(opp_table, new_opp, opp);
                if (opp_cmp > 0) {
                        *head = &opp->node;
                        continue;
 
                /* Duplicate OPPs */
                dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n",
-                        __func__, opp->rate, opp->supplies[0].u_volt,
-                        opp->available, new_opp->rate,
+                        __func__, opp->rates[0], opp->supplies[0].u_volt,
+                        opp->available, new_opp->rates[0],
                         new_opp->supplies[0].u_volt, new_opp->available);
 
                /* Should we compare voltages for all regulators here ? */
 
                opp->available = false;
                pr_warn("%s: OPP not supported by required OPP %pOF (%lu)\n",
-                        __func__, opp->required_opps[i]->np, opp->rate);
+                        __func__, opp->required_opps[i]->np, opp->rates[0]);
                return;
        }
 }
        if (!_opp_supported_by_regulators(new_opp, opp_table)) {
                new_opp->available = false;
                dev_warn(dev, "%s: OPP not supported by regulators (%lu)\n",
-                        __func__, new_opp->rate);
+                        __func__, new_opp->rates[0]);
        }
 
        /* required-opps not fully initialized yet */
                return -ENOMEM;
 
        /* populate the opp table */
-       new_opp->rate = freq;
+       new_opp->rates[0] = freq;
        tol = u_volt * opp_table->voltage_tolerance_v1 / 100;
        new_opp->supplies[0].u_volt = u_volt;
        new_opp->supplies[0].u_volt_min = u_volt - tol;
        opp_table->regulator_count = -1;
 }
 
+static void _put_clks(struct opp_table *opp_table, int count)
+{
+       int i;
+
+       for (i = count - 1; i >= 0; i--)
+               clk_put(opp_table->clks[i]);
+
+       kfree(opp_table->clks);
+       opp_table->clks = NULL;
+}
+
 /**
  * _opp_set_clknames() - Set clk names for the device
  * @dev: Device for which clk names is being set.
  * This must be called before any OPPs are initialized for the device.
  */
 static int _opp_set_clknames(struct opp_table *opp_table, struct device *dev,
-                            const char * const names[])
+                            const char * const names[],
+                            config_clks_t config_clks)
 {
        const char * const *temp = names;
-       int count = 0;
+       int count = 0, ret, i;
+       struct clk *clk;
 
        /* Count number of clks */
        while (*temp++)
        if (!count && !names[1])
                count = 1;
 
-       /* We support only one clock name for now */
-       if (count != 1)
+       /* Fail early for invalid configurations */
+       if (!count || (config_clks && count == 1) || (!config_clks && count > 1))
                return -EINVAL;
 
        /* Another CPU that shares the OPP table has set the clkname ? */
-       if (opp_table->clk_configured)
+       if (opp_table->clks)
                return 0;
 
-       /* clk shouldn't be initialized at this point */
-       if (WARN_ON(opp_table->clk))
-               return -EBUSY;
+       opp_table->clks = kmalloc_array(count, sizeof(*opp_table->clks),
+                                       GFP_KERNEL);
+       if (!opp_table->clks)
+               return -ENOMEM;
 
-       /* Find clk for the device */
-       opp_table->clk = clk_get(dev, names[0]);
-       if (IS_ERR(opp_table->clk)) {
-               return dev_err_probe(dev, PTR_ERR(opp_table->clk),
-                                   "%s: Couldn't find clock\n", __func__);
+       /* Find clks for the device */
+       for (i = 0; i < count; i++) {
+               clk = clk_get(dev, names[i]);
+               if (IS_ERR(clk)) {
+                       ret = dev_err_probe(dev, PTR_ERR(clk),
+                                           "%s: Couldn't find clock with name: %s\n",
+                                           __func__, names[i]);
+                       goto free_clks;
+               }
+
+               opp_table->clks[i] = clk;
        }
 
-       opp_table->clk_configured = true;
+       opp_table->clk_count = count;
+
+       /* Set generic single clk set here */
+       if (count == 1) {
+               opp_table->config_clks = _opp_config_clk_single;
+
+               /*
+                * We could have just dropped the "clk" field and used "clks"
+                * everywhere. Instead we kept the "clk" field around for
+                * following reasons:
+                *
+                * - avoiding clks[0] everywhere else.
+                * - not running single clk helpers for multiple clk usecase by
+                *   mistake.
+                *
+                * Since this is single-clk case, just update the clk pointer
+                * too.
+                */
+               opp_table->clk = opp_table->clks[0];
+       } else {
+               opp_table->config_clks = config_clks;
+       }
 
        return 0;
+
+free_clks:
+       _put_clks(opp_table, i);
+       return ret;
 }
 
 /**
  */
 static void _opp_put_clknames(struct opp_table *opp_table)
 {
-       if (opp_table->clk_configured) {
-               clk_put(opp_table->clk);
-               opp_table->clk = ERR_PTR(-EINVAL);
-               opp_table->clk_configured = false;
-       }
+       if (!opp_table->clks)
+               return;
+
+       opp_table->config_clks = NULL;
+       opp_table->clk = ERR_PTR(-ENODEV);
+
+       _put_clks(opp_table, opp_table->clk_count);
 }
 
 /**
 
        /* Configure clocks */
        if (config->clk_names) {
-               ret = _opp_set_clknames(opp_table, dev, config->clk_names);
+               ret = _opp_set_clknames(opp_table, dev, config->clk_names,
+                                       config->config_clks);
                if (ret)
                        goto err;
 
                data->flags |= OPP_CONFIG_CLK;
+       } else if (config->config_clks) {
+               /* Don't allow config callback without clocks */
+               ret = -EINVAL;
+               goto err;
        }
 
        /* Configure property names */
 
        /* Do we have the frequency? */
        list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
-               if (tmp_opp->rate == freq) {
+               if (tmp_opp->rates[0] == freq) {
                        opp = tmp_opp;
                        break;
                }
 
        /* Do we have the frequency? */
        list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
-               if (tmp_opp->rate == freq) {
+               if (tmp_opp->rates[0] == freq) {
                        opp = tmp_opp;
                        break;
                }
 
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
 
+static int _read_rate(struct dev_pm_opp *new_opp, struct opp_table *opp_table,
+                     struct device_node *np)
+{
+       struct property *prop;
+       int i, count, ret;
+       u64 *rates;
+
+       prop = of_find_property(np, "opp-hz", NULL);
+       if (!prop)
+               return -ENODEV;
+
+       count = prop->length / sizeof(u64);
+       if (opp_table->clk_count != count) {
+               pr_err("%s: Count mismatch between opp-hz and clk_count (%d %d)\n",
+                      __func__, count, opp_table->clk_count);
+               return -EINVAL;
+       }
+
+       rates = kmalloc_array(count, sizeof(*rates), GFP_KERNEL);
+       if (!rates)
+               return -ENOMEM;
+
+       ret = of_property_read_u64_array(np, "opp-hz", rates, count);
+       if (ret) {
+               pr_err("%s: Error parsing opp-hz: %d\n", __func__, ret);
+       } else {
+               /*
+                * 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.
+                */
+               for (i = 0; i < count; i++) {
+                       new_opp->rates[i] = (unsigned long)rates[i];
+
+                       /* This will happen for frequencies > 4.29 GHz */
+                       WARN_ON(new_opp->rates[i] != rates[i]);
+               }
+       }
+
+       kfree(rates);
+
+       return ret;
+}
+
 static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *opp_table,
                    struct device_node *np, bool peak)
 {
                         struct opp_table *opp_table, struct device_node *np)
 {
        bool found = false;
-       u64 rate;
        int ret;
 
-       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;
+       ret = _read_rate(new_opp, opp_table, np);
+       if (!ret)
                found = true;
-       }
+       else if (ret != -ENODEV)
+               return ret;
 
        /*
         * Bandwidth consists of peak and average (optional) values:
 
        /* Check if the OPP supports hardware's hierarchy of versions or not */
        if (!_opp_is_supported(dev, opp_table, np)) {
-               dev_dbg(dev, "OPP not supported by hardware: %lu\n",
-                       new_opp->rate);
+               dev_dbg(dev, "OPP not supported by hardware: %s\n",
+                       of_node_full_name(np));
                goto free_opp;
        }
 
        if (of_property_read_bool(np, "opp-suspend")) {
                if (opp_table->suspend_opp) {
                        /* Pick the OPP with higher rate/bw/level as suspend OPP */
-                       if (_opp_compare_key(new_opp, opp_table->suspend_opp) == 1) {
+                       if (_opp_compare_key(opp_table, new_opp, opp_table->suspend_opp) == 1) {
                                opp_table->suspend_opp->suspend = false;
                                new_opp->suspend = true;
                                opp_table->suspend_opp = new_opp;
                opp_table->clock_latency_ns_max = new_opp->clock_latency_ns;
 
        pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu level:%u\n",
-                __func__, new_opp->turbo, new_opp->rate,
+                __func__, new_opp->turbo, new_opp->rates[0],
                 new_opp->supplies[0].u_volt, new_opp->supplies[0].u_volt_min,
                 new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns,
                 new_opp->level);