Commit dfbe4678 authored by Viresh Kumar's avatar Viresh Kumar Committed by Rafael J. Wysocki

PM / OPP: Add infrastructure to manage multiple regulators

This patch adds infrastructure to manage multiple regulators and updates
the only user (cpufreq-dt) of dev_pm_opp_set{put}_regulator().

This is preparatory work for adding full support for devices with
multiple regulators.
Signed-off-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
Tested-by: default avatarDave Gerlach <d-gerlach@ti.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent ce31781a
This diff is collapsed.
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/limits.h> #include <linux/limits.h>
#include <linux/slab.h>
#include "opp.h" #include "opp.h"
...@@ -34,6 +35,46 @@ void opp_debug_remove_one(struct dev_pm_opp *opp) ...@@ -34,6 +35,46 @@ void opp_debug_remove_one(struct dev_pm_opp *opp)
debugfs_remove_recursive(opp->dentry); debugfs_remove_recursive(opp->dentry);
} }
static bool opp_debug_create_supplies(struct dev_pm_opp *opp,
struct opp_table *opp_table,
struct dentry *pdentry)
{
struct dentry *d;
int i = 0;
char *name;
/* Always create at least supply-0 directory */
do {
name = kasprintf(GFP_KERNEL, "supply-%d", i);
/* Create per-opp directory */
d = debugfs_create_dir(name, pdentry);
kfree(name);
if (!d)
return false;
if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d,
&opp->supplies[i].u_volt))
return false;
if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d,
&opp->supplies[i].u_volt_min))
return false;
if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d,
&opp->supplies[i].u_volt_max))
return false;
if (!debugfs_create_ulong("u_amp", S_IRUGO, d,
&opp->supplies[i].u_amp))
return false;
} while (++i < opp_table->regulator_count);
return true;
}
int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
{ {
struct dentry *pdentry = opp_table->dentry; struct dentry *pdentry = opp_table->dentry;
...@@ -63,16 +104,7 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) ...@@ -63,16 +104,7 @@ 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)) if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate))
return -ENOMEM; return -ENOMEM;
if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d, &opp->supply.u_volt)) if (!opp_debug_create_supplies(opp, opp_table, d))
return -ENOMEM;
if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d, &opp->supply.u_volt_min))
return -ENOMEM;
if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d, &opp->supply.u_volt_max))
return -ENOMEM;
if (!debugfs_create_ulong("u_amp", S_IRUGO, d, &opp->supply.u_amp))
return -ENOMEM; return -ENOMEM;
if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d, if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/slab.h>
#include <linux/export.h> #include <linux/export.h>
#include "opp.h" #include "opp.h"
...@@ -101,16 +102,16 @@ static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table, ...@@ -101,16 +102,16 @@ static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
return true; return true;
} }
/* TODO: Support multiple regulators */
static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
struct opp_table *opp_table) struct opp_table *opp_table)
{ {
u32 microvolt[3] = {0}; u32 *microvolt, *microamp = NULL;
u32 val; int supplies, vcount, icount, ret, i, j;
int count, ret;
struct property *prop = NULL; struct property *prop = NULL;
char name[NAME_MAX]; char name[NAME_MAX];
supplies = opp_table->regulator_count ? opp_table->regulator_count : 1;
/* Search for "opp-microvolt-<name>" */ /* Search for "opp-microvolt-<name>" */
if (opp_table->prop_name) { if (opp_table->prop_name) {
snprintf(name, sizeof(name), "opp-microvolt-%s", snprintf(name, sizeof(name), "opp-microvolt-%s",
...@@ -128,34 +129,29 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, ...@@ -128,34 +129,29 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
return 0; return 0;
} }
count = of_property_count_u32_elems(opp->np, name); vcount = of_property_count_u32_elems(opp->np, name);
if (count < 0) { if (vcount < 0) {
dev_err(dev, "%s: Invalid %s property (%d)\n", dev_err(dev, "%s: Invalid %s property (%d)\n",
__func__, name, count); __func__, name, vcount);
return count; return vcount;
} }
/* There can be one or three elements here */ /* There can be one or three elements per supply */
if (count != 1 && count != 3) { if (vcount != supplies && vcount != supplies * 3) {
dev_err(dev, "%s: Invalid number of elements in %s property (%d)\n", dev_err(dev, "%s: Invalid number of elements in %s property (%d) with supplies (%d)\n",
__func__, name, count); __func__, name, vcount, supplies);
return -EINVAL; return -EINVAL;
} }
ret = of_property_read_u32_array(opp->np, name, microvolt, count); microvolt = kmalloc_array(vcount, sizeof(*microvolt), GFP_KERNEL);
if (!microvolt)
return -ENOMEM;
ret = of_property_read_u32_array(opp->np, name, microvolt, vcount);
if (ret) { if (ret) {
dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret); dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret);
return -EINVAL; ret = -EINVAL;
} goto free_microvolt;
opp->supply.u_volt = microvolt[0];
if (count == 1) {
opp->supply.u_volt_min = opp->supply.u_volt;
opp->supply.u_volt_max = opp->supply.u_volt;
} else {
opp->supply.u_volt_min = microvolt[1];
opp->supply.u_volt_max = microvolt[2];
} }
/* Search for "opp-microamp-<name>" */ /* Search for "opp-microamp-<name>" */
...@@ -172,10 +168,59 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, ...@@ -172,10 +168,59 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
prop = of_find_property(opp->np, name, NULL); prop = of_find_property(opp->np, name, NULL);
} }
if (prop && !of_property_read_u32(opp->np, name, &val)) if (prop) {
opp->supply.u_amp = val; icount = of_property_count_u32_elems(opp->np, name);
if (icount < 0) {
dev_err(dev, "%s: Invalid %s property (%d)\n", __func__,
name, icount);
ret = icount;
goto free_microvolt;
}
return 0; if (icount != supplies) {
dev_err(dev, "%s: Invalid number of elements in %s property (%d) with supplies (%d)\n",
__func__, name, icount, supplies);
ret = -EINVAL;
goto free_microvolt;
}
microamp = kmalloc_array(icount, sizeof(*microamp), GFP_KERNEL);
if (!microamp) {
ret = -EINVAL;
goto free_microvolt;
}
ret = of_property_read_u32_array(opp->np, name, microamp,
icount);
if (ret) {
dev_err(dev, "%s: error parsing %s: %d\n", __func__,
name, ret);
ret = -EINVAL;
goto free_microamp;
}
}
for (i = 0, j = 0; i < supplies; i++) {
opp->supplies[i].u_volt = microvolt[j++];
if (vcount == supplies) {
opp->supplies[i].u_volt_min = opp->supplies[i].u_volt;
opp->supplies[i].u_volt_max = opp->supplies[i].u_volt;
} else {
opp->supplies[i].u_volt_min = microvolt[j++];
opp->supplies[i].u_volt_max = microvolt[j++];
}
if (microamp)
opp->supplies[i].u_amp = microamp[i];
}
free_microamp:
kfree(microamp);
free_microvolt:
kfree(microvolt);
return ret;
} }
/** /**
...@@ -304,8 +349,8 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) ...@@ -304,8 +349,8 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n", pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n",
__func__, new_opp->turbo, new_opp->rate, __func__, new_opp->turbo, new_opp->rate,
new_opp->supply.u_volt, new_opp->supply.u_volt_min, new_opp->supplies[0].u_volt, new_opp->supplies[0].u_volt_min,
new_opp->supply.u_volt_max, new_opp->clock_latency_ns); new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns);
/* /*
* Notify the changes in the availability of the operable * Notify the changes in the availability of the operable
......
...@@ -61,7 +61,7 @@ extern struct list_head opp_tables; ...@@ -61,7 +61,7 @@ extern struct list_head opp_tables;
* @turbo: true if turbo (boost) OPP * @turbo: true if turbo (boost) OPP
* @suspend: true if suspend OPP * @suspend: true if suspend OPP
* @rate: Frequency in hertz * @rate: Frequency in hertz
* @supply: Power supply voltage/current values * @supplies: Power supplies voltage/current values
* @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
* frequency from any other OPP's frequency. * frequency from any other OPP's frequency.
* @opp_table: points back to the opp_table struct this opp belongs to * @opp_table: points back to the opp_table struct this opp belongs to
...@@ -80,7 +80,7 @@ struct dev_pm_opp { ...@@ -80,7 +80,7 @@ struct dev_pm_opp {
bool suspend; bool suspend;
unsigned long rate; unsigned long rate;
struct dev_pm_opp_supply supply; struct dev_pm_opp_supply *supplies;
unsigned long clock_latency_ns; unsigned long clock_latency_ns;
...@@ -139,7 +139,8 @@ enum opp_table_access { ...@@ -139,7 +139,8 @@ enum opp_table_access {
* @supported_hw_count: Number of elements in supported_hw array. * @supported_hw_count: Number of elements in supported_hw array.
* @prop_name: A name to postfix to many DT properties, while parsing them. * @prop_name: A name to postfix to many DT properties, while parsing them.
* @clk: Device's clock handle * @clk: Device's clock handle
* @regulator: Supply regulator * @regulators: Supply regulators
* @regulator_count: Number of power supply regulators
* @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry: debugfs dentry pointer of the real device directory (not links).
* @dentry_name: Name of the real dentry. * @dentry_name: Name of the real dentry.
* *
...@@ -174,7 +175,8 @@ struct opp_table { ...@@ -174,7 +175,8 @@ struct opp_table {
unsigned int supported_hw_count; unsigned int supported_hw_count;
const char *prop_name; const char *prop_name;
struct clk *clk; struct clk *clk;
struct regulator *regulator; struct regulator **regulators;
unsigned int regulator_count;
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
struct dentry *dentry; struct dentry *dentry;
......
...@@ -188,7 +188,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) ...@@ -188,7 +188,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
*/ */
name = find_supply_name(cpu_dev); name = find_supply_name(cpu_dev);
if (name) { if (name) {
opp_table = dev_pm_opp_set_regulator(cpu_dev, name); opp_table = dev_pm_opp_set_regulators(cpu_dev, &name, 1);
if (IS_ERR(opp_table)) { if (IS_ERR(opp_table)) {
ret = PTR_ERR(opp_table); ret = PTR_ERR(opp_table);
dev_err(cpu_dev, "Failed to set regulator for cpu%d: %d\n", dev_err(cpu_dev, "Failed to set regulator for cpu%d: %d\n",
...@@ -289,7 +289,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) ...@@ -289,7 +289,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
out_free_opp: out_free_opp:
dev_pm_opp_of_cpumask_remove_table(policy->cpus); dev_pm_opp_of_cpumask_remove_table(policy->cpus);
if (name) if (name)
dev_pm_opp_put_regulator(opp_table); dev_pm_opp_put_regulators(opp_table);
out_put_clk: out_put_clk:
clk_put(cpu_clk); clk_put(cpu_clk);
...@@ -304,7 +304,7 @@ static int cpufreq_exit(struct cpufreq_policy *policy) ...@@ -304,7 +304,7 @@ static int cpufreq_exit(struct cpufreq_policy *policy)
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
if (priv->reg_name) if (priv->reg_name)
dev_pm_opp_put_regulator(priv->opp_table); dev_pm_opp_put_regulators(priv->opp_table);
clk_put(policy->clk); clk_put(policy->clk);
kfree(priv); kfree(priv);
......
...@@ -79,8 +79,8 @@ int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, ...@@ -79,8 +79,8 @@ int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
void dev_pm_opp_put_supported_hw(struct device *dev); void dev_pm_opp_put_supported_hw(struct device *dev);
int dev_pm_opp_set_prop_name(struct device *dev, const char *name); int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
void dev_pm_opp_put_prop_name(struct device *dev); void dev_pm_opp_put_prop_name(struct device *dev);
struct opp_table *dev_pm_opp_set_regulator(struct device *dev, const char *name); struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count);
void dev_pm_opp_put_regulator(struct opp_table *opp_table); void dev_pm_opp_put_regulators(struct opp_table *opp_table);
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); 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_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); int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
...@@ -187,12 +187,12 @@ static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name) ...@@ -187,12 +187,12 @@ static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
static inline void dev_pm_opp_put_prop_name(struct device *dev) {} static inline void dev_pm_opp_put_prop_name(struct device *dev) {}
static inline struct opp_table *dev_pm_opp_set_regulator(struct device *dev, const char *name) static inline struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count)
{ {
return ERR_PTR(-ENOTSUPP); return ERR_PTR(-ENOTSUPP);
} }
static inline void dev_pm_opp_put_regulator(struct opp_table *opp_table) {} static inline void dev_pm_opp_put_regulators(struct opp_table *opp_table) {}
static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment