Commit 2083da24 authored by Viresh Kumar's avatar Viresh Kumar

OPP: Allow multiple clocks for a device

This patch adds support to allow multiple clocks for a device.

The design is pretty much similar to how this is done for regulators,
and platforms can supply their own version of the config_clks() callback
if they have multiple clocks for their device. The core manages the
calls via opp_table->config_clks() eventually.

We have kept both "clk" and "clks" fields in the OPP table structure and
the reason is provided as a comment in _opp_set_clknames(). The same
isn't done for "rates" though and we use rates[0] at most of the places
now.
Co-developed-by: default avatarKrzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: default avatarKrzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Tested-by: default avatarJon Hunter <jonathanh@nvidia.com>
Tested-by: default avatarDmitry Osipenko <dmitry.osipenko@collabora.com>
Tested-by: default avatarManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
parent 3cb16ad6
This diff is collapsed.
...@@ -74,6 +74,24 @@ static void opp_debug_create_bw(struct dev_pm_opp *opp, ...@@ -74,6 +74,24 @@ static void opp_debug_create_bw(struct dev_pm_opp *opp,
} }
} }
static void opp_debug_create_clks(struct dev_pm_opp *opp,
struct opp_table *opp_table,
struct dentry *pdentry)
{
char name[12];
int i;
if (opp_table->clk_count == 1) {
debugfs_create_ulong("rate_hz", S_IRUGO, pdentry, &opp->rates[0]);
return;
}
for (i = 0; i < opp_table->clk_count; i++) {
snprintf(name, sizeof(name), "rate_hz_%d", i);
debugfs_create_ulong(name, S_IRUGO, pdentry, &opp->rates[i]);
}
}
static void opp_debug_create_supplies(struct dev_pm_opp *opp, static void opp_debug_create_supplies(struct dev_pm_opp *opp,
struct opp_table *opp_table, struct opp_table *opp_table,
struct dentry *pdentry) struct dentry *pdentry)
...@@ -117,10 +135,11 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) ...@@ -117,10 +135,11 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
* Get directory name for OPP. * Get directory name for OPP.
* *
* - Normally rate is unique to each OPP, use it to get unique opp-name. * - Normally rate is unique to each OPP, use it to get unique opp-name.
* - For some devices rate isn't available, use index instead. * - For some devices rate isn't available or there are multiple, use
* index instead for them.
*/ */
if (likely(opp->rate)) if (likely(opp_table->clk_count == 1 && opp->rates[0]))
id = opp->rate; id = opp->rates[0];
else else
id = _get_opp_count(opp_table); id = _get_opp_count(opp_table);
...@@ -134,7 +153,6 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) ...@@ -134,7 +153,6 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo); debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo);
debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend); debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend);
debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate); debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate);
debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate);
debugfs_create_u32("level", S_IRUGO, d, &opp->level); debugfs_create_u32("level", S_IRUGO, d, &opp->level);
debugfs_create_ulong("clock_latency_ns", S_IRUGO, d, debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
&opp->clock_latency_ns); &opp->clock_latency_ns);
...@@ -142,6 +160,7 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) ...@@ -142,6 +160,7 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
opp->of_name = of_node_full_name(opp->np); opp->of_name = of_node_full_name(opp->np);
debugfs_create_str("of_name", S_IRUGO, d, (char **)&opp->of_name); debugfs_create_str("of_name", S_IRUGO, d, (char **)&opp->of_name);
opp_debug_create_clks(opp, opp_table, d);
opp_debug_create_supplies(opp, opp_table, d); opp_debug_create_supplies(opp, opp_table, d);
opp_debug_create_bw(opp, opp_table, d); opp_debug_create_bw(opp, opp_table, d);
......
...@@ -767,6 +767,50 @@ void dev_pm_opp_of_remove_table(struct device *dev) ...@@ -767,6 +767,50 @@ void dev_pm_opp_of_remove_table(struct device *dev)
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); 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, static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *opp_table,
struct device_node *np, bool peak) struct device_node *np, bool peak)
{ {
...@@ -812,19 +856,13 @@ static int _read_opp_key(struct dev_pm_opp *new_opp, ...@@ -812,19 +856,13 @@ static int _read_opp_key(struct dev_pm_opp *new_opp,
struct opp_table *opp_table, struct device_node *np) struct opp_table *opp_table, struct device_node *np)
{ {
bool found = false; bool found = false;
u64 rate;
int ret; int ret;
ret = of_property_read_u64(np, "opp-hz", &rate); ret = _read_rate(new_opp, opp_table, np);
if (!ret) { 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;
found = true; found = true;
} else if (ret != -ENODEV)
return ret;
/* /*
* Bandwidth consists of peak and average (optional) values: * Bandwidth consists of peak and average (optional) values:
...@@ -893,8 +931,8 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, ...@@ -893,8 +931,8 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
/* Check if the OPP supports hardware's hierarchy of versions or not */ /* Check if the OPP supports hardware's hierarchy of versions or not */
if (!_opp_is_supported(dev, opp_table, np)) { if (!_opp_is_supported(dev, opp_table, np)) {
dev_dbg(dev, "OPP not supported by hardware: %lu\n", dev_dbg(dev, "OPP not supported by hardware: %s\n",
new_opp->rate); of_node_full_name(np));
goto free_opp; goto free_opp;
} }
...@@ -930,7 +968,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, ...@@ -930,7 +968,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
if (of_property_read_bool(np, "opp-suspend")) { if (of_property_read_bool(np, "opp-suspend")) {
if (opp_table->suspend_opp) { if (opp_table->suspend_opp) {
/* Pick the OPP with higher rate/bw/level as 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; opp_table->suspend_opp->suspend = false;
new_opp->suspend = true; new_opp->suspend = true;
opp_table->suspend_opp = new_opp; opp_table->suspend_opp = new_opp;
...@@ -945,7 +983,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, ...@@ -945,7 +983,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
opp_table->clock_latency_ns_max = new_opp->clock_latency_ns; 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", 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, new_opp->supplies[0].u_volt_min,
new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns, new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns,
new_opp->level); new_opp->level);
......
...@@ -79,7 +79,7 @@ struct opp_config_data { ...@@ -79,7 +79,7 @@ struct opp_config_data {
* @suspend: true if suspend OPP * @suspend: true if suspend OPP
* @removed: flag indicating that OPP's reference is dropped by OPP core. * @removed: flag indicating that OPP's reference is dropped by OPP core.
* @pstate: Device's power domain's performance state. * @pstate: Device's power domain's performance state.
* @rate: Frequency in hertz * @rates: Frequencies in hertz
* @level: Performance level * @level: Performance level
* @supplies: Power supplies voltage/current values * @supplies: Power supplies voltage/current values
* @bandwidth: Interconnect bandwidth values * @bandwidth: Interconnect bandwidth values
...@@ -102,7 +102,7 @@ struct dev_pm_opp { ...@@ -102,7 +102,7 @@ struct dev_pm_opp {
bool suspend; bool suspend;
bool removed; bool removed;
unsigned int pstate; unsigned int pstate;
unsigned long rate; unsigned long *rates;
unsigned int level; unsigned int level;
struct dev_pm_opp_supply *supplies; struct dev_pm_opp_supply *supplies;
...@@ -170,8 +170,10 @@ enum opp_table_access { ...@@ -170,8 +170,10 @@ enum opp_table_access {
* @supported_hw: Array of version number to support. * @supported_hw: Array of version number to support.
* @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_configured: Clock name is configured by the platform. * @config_clks: Platform specific config_clks() callback.
* @clk: Device's clock handle * @clks: Device's clock handles, for multiple clocks.
* @clk: Device's clock handle, for single clock.
* @clk_count: Number of clocks.
* @config_regulators: Platform specific config_regulators() callback. * @config_regulators: Platform specific config_regulators() callback.
* @regulators: Supply regulators * @regulators: Supply regulators
* @regulator_count: Number of power supply regulators. Its value can be -1 * @regulator_count: Number of power supply regulators. Its value can be -1
...@@ -220,8 +222,10 @@ struct opp_table { ...@@ -220,8 +222,10 @@ struct opp_table {
unsigned int *supported_hw; unsigned int *supported_hw;
unsigned int supported_hw_count; unsigned int supported_hw_count;
const char *prop_name; const char *prop_name;
bool clk_configured; config_clks_t config_clks;
struct clk **clks;
struct clk *clk; struct clk *clk;
int clk_count;
config_regulators_t config_regulators; config_regulators_t config_regulators;
struct regulator **regulators; struct regulator **regulators;
int regulator_count; int regulator_count;
...@@ -246,7 +250,7 @@ struct opp_table *_find_opp_table(struct device *dev); ...@@ -246,7 +250,7 @@ struct opp_table *_find_opp_table(struct device *dev);
struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table); struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table);
struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table); struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table);
void _opp_free(struct dev_pm_opp *opp); void _opp_free(struct dev_pm_opp *opp);
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);
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table); int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table);
int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic); int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic);
void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu); void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu);
......
...@@ -61,9 +61,13 @@ typedef int (*config_regulators_t)(struct device *dev, ...@@ -61,9 +61,13 @@ typedef int (*config_regulators_t)(struct device *dev,
struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp, struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp,
struct regulator **regulators, unsigned int count); struct regulator **regulators, unsigned int count);
typedef int (*config_clks_t)(struct device *dev, struct opp_table *opp_table,
struct dev_pm_opp *opp, void *data, bool scaling_down);
/** /**
* struct dev_pm_opp_config - Device OPP configuration values * struct dev_pm_opp_config - Device OPP configuration values
* @clk_names: Clk names, NULL terminated array, max 1 clock for now. * @clk_names: Clk names, NULL terminated array.
* @config_clks: Custom set clk helper.
* @prop_name: Name to postfix to properties. * @prop_name: Name to postfix to properties.
* @config_regulators: Custom set regulator helper. * @config_regulators: Custom set regulator helper.
* @supported_hw: Array of hierarchy of versions to match. * @supported_hw: Array of hierarchy of versions to match.
...@@ -78,6 +82,7 @@ typedef int (*config_regulators_t)(struct device *dev, ...@@ -78,6 +82,7 @@ typedef int (*config_regulators_t)(struct device *dev,
struct dev_pm_opp_config { struct dev_pm_opp_config {
/* NULL terminated */ /* NULL terminated */
const char * const *clk_names; const char * const *clk_names;
config_clks_t config_clks;
const char *prop_name; const char *prop_name;
config_regulators_t config_regulators; config_regulators_t config_regulators;
const unsigned int *supported_hw; const unsigned int *supported_hw;
......
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