clk: Add support to vote to regulator framework from clk framework

Add vdd_class support which would help vote/unvote for any voltage rail
for the clock frequency to the regulator framework. A clock client request
for a clock frequency would look for the corresponding voltage vote and
would be send the request to regulator framework.

Change-Id: I5b1229091fcb7b3887b54735b9663fd31a35db21
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Taniya Das <tdas@codeaurora.org>
Signed-off-by: Deepak Katragadda <dkatraga@codeaurora.org>
Signed-off-by: David Dai <daidavid1@codeaurora.org>
This commit is contained in:
Stephen Boyd 2016-05-16 11:05:16 +05:30 committed by David Dai
parent 8ff1a590c2
commit f349485712
10 changed files with 370 additions and 14 deletions

View File

@ -219,7 +219,7 @@ struct clk_hw *clk_hw_register_composite(struct device *dev, const char *name,
unsigned long flags)
{
struct clk_hw *hw;
struct clk_init_data init;
struct clk_init_data init = {};
struct clk_composite *composite;
struct clk_ops *clk_composite_ops;
int ret;

View File

@ -458,7 +458,7 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name,
{
struct clk_divider *div;
struct clk_hw *hw;
struct clk_init_data init;
struct clk_init_data init = {};
int ret;
if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {

View File

@ -74,7 +74,7 @@ struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
unsigned int mult, unsigned int div)
{
struct clk_fixed_factor *fix;
struct clk_init_data init;
struct clk_init_data init = {};
struct clk_hw *hw;
int ret;

View File

@ -61,7 +61,7 @@ struct clk_hw *clk_hw_register_fixed_rate_with_accuracy(struct device *dev,
{
struct clk_fixed_rate *fixed;
struct clk_hw *hw;
struct clk_init_data init;
struct clk_init_data init = {};
int ret;
/* allocate fixed-rate clock */

View File

@ -134,7 +134,7 @@ struct clk_hw *clk_hw_register_fractional_divider(struct device *dev,
u8 clk_divider_flags, spinlock_t *lock)
{
struct clk_fractional_divider *fd;
struct clk_init_data init;
struct clk_init_data init = {};
struct clk_hw *hw;
int ret;

View File

@ -128,7 +128,7 @@ struct clk_hw *clk_hw_register_gate(struct device *dev, const char *name,
{
struct clk_gate *gate;
struct clk_hw *hw;
struct clk_init_data init;
struct clk_init_data init = {};
int ret;
if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {

View File

@ -140,7 +140,7 @@ struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name,
{
struct clk_mux *mux;
struct clk_hw *hw;
struct clk_init_data init;
struct clk_init_data init = {};
u8 width = 0;
int ret;

View File

@ -56,7 +56,7 @@ static const struct clk_ops clk_pwm_ops = {
static int clk_pwm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct clk_init_data init;
struct clk_init_data init = {};
struct clk_pwm *clk_pwm;
struct pwm_device *pwm;
struct pwm_args pargs;

View File

@ -1,6 +1,7 @@
/*
* Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
* Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org>
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -24,6 +25,7 @@
#include <linux/pm_runtime.h>
#include <linux/sched.h>
#include <linux/clkdev.h>
#include <linux/regulator/consumer.h>
#include "clk.h"
@ -40,6 +42,13 @@ static HLIST_HEAD(clk_root_list);
static HLIST_HEAD(clk_orphan_list);
static LIST_HEAD(clk_notifier_list);
struct clk_handoff_vdd {
struct list_head list;
struct clk_vdd_class *vdd_class;
};
static LIST_HEAD(clk_handoff_vdd_list);
/*** private data structures ***/
struct clk_core {
@ -77,6 +86,9 @@ struct clk_core {
struct hlist_node debug_node;
#endif
struct kref ref;
struct clk_vdd_class *vdd_class;
unsigned long *rate_max;
int num_rate_max;
};
#define CREATE_TRACE_POINTS
@ -560,6 +572,225 @@ int __clk_mux_determine_rate_closest(struct clk_hw *hw,
}
EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest);
/*
* Find the voltage level required for a given clock rate.
*/
static int clk_find_vdd_level(struct clk_core *clk, unsigned long rate)
{
int level;
/*
* For certain PLLs, due to the limitation in the bits allocated for
* programming the fractional divider, the actual rate of the PLL will
* be slightly higher than the requested rate (in the order of several
* Hz). To accommodate this difference, convert the FMAX rate and the
* clock frequency to KHz and use that for deriving the voltage level.
*/
for (level = 0; level < clk->num_rate_max; level++)
if (DIV_ROUND_CLOSEST(rate, 1000) <=
DIV_ROUND_CLOSEST(clk->rate_max[level], 1000))
break;
if (level == clk->num_rate_max) {
pr_err("Rate %lu for %s is greater than highest Fmax\n", rate,
clk->name);
return -EINVAL;
}
return level;
}
/*
* Update voltage level given the current votes.
*/
static int clk_update_vdd(struct clk_vdd_class *vdd_class)
{
int level, rc = 0, i, ignore;
struct regulator **r = vdd_class->regulator;
int *uv = vdd_class->vdd_uv;
int n_reg = vdd_class->num_regulators;
int cur_lvl = vdd_class->cur_level;
int max_lvl = vdd_class->num_levels - 1;
int cur_base = cur_lvl * n_reg;
int new_base;
/* aggregate votes */
for (level = max_lvl; level > 0; level--)
if (vdd_class->level_votes[level])
break;
if (level == cur_lvl)
return 0;
max_lvl = max_lvl * n_reg;
new_base = level * n_reg;
for (i = 0; i < vdd_class->num_regulators; i++) {
pr_debug("Set Voltage level Min %d, Max %d\n", uv[new_base + i],
uv[max_lvl + i]);
rc = regulator_set_voltage(r[i], uv[new_base + i],
vdd_class->use_max_uV ? INT_MAX : uv[max_lvl + i]);
if (rc)
goto set_voltage_fail;
if (cur_lvl == 0 || cur_lvl == vdd_class->num_levels)
rc = regulator_enable(r[i]);
else if (level == 0)
rc = regulator_disable(r[i]);
if (rc)
goto enable_disable_fail;
}
if (vdd_class->set_vdd && !vdd_class->num_regulators)
rc = vdd_class->set_vdd(vdd_class, level);
if (!rc)
vdd_class->cur_level = level;
return rc;
enable_disable_fail:
regulator_set_voltage(r[i], uv[cur_base + i],
vdd_class->use_max_uV ? INT_MAX : uv[max_lvl + i]);
set_voltage_fail:
for (i--; i >= 0; i--) {
regulator_set_voltage(r[i], uv[cur_base + i],
vdd_class->use_max_uV ? INT_MAX : uv[max_lvl + i]);
if (cur_lvl == 0 || cur_lvl == vdd_class->num_levels)
regulator_disable(r[i]);
else if (level == 0)
ignore = regulator_enable(r[i]);
}
return rc;
}
/*
* Vote for a voltage level.
*/
static int clk_vote_vdd_level(struct clk_vdd_class *vdd_class, int level)
{
int rc = 0;
if (level >= vdd_class->num_levels)
return -EINVAL;
mutex_lock(&vdd_class->lock);
vdd_class->level_votes[level]++;
rc = clk_update_vdd(vdd_class);
if (rc)
vdd_class->level_votes[level]--;
mutex_unlock(&vdd_class->lock);
return rc;
}
/*
* Remove vote for a voltage level.
*/
static int clk_unvote_vdd_level(struct clk_vdd_class *vdd_class, int level)
{
int rc = 0;
if (level >= vdd_class->num_levels)
return -EINVAL;
mutex_lock(&vdd_class->lock);
if (WARN(!vdd_class->level_votes[level],
"Reference counts are incorrect for %s level %d\n",
vdd_class->class_name, level))
goto out;
vdd_class->level_votes[level]--;
rc = clk_update_vdd(vdd_class);
if (rc)
vdd_class->level_votes[level]++;
out:
mutex_unlock(&vdd_class->lock);
return rc;
}
/*
* Vote for a voltage level corresponding to a clock's rate.
*/
static int clk_vote_rate_vdd(struct clk_core *core, unsigned long rate)
{
int level;
if (!core->vdd_class)
return 0;
level = clk_find_vdd_level(core, rate);
if (level < 0)
return level;
return clk_vote_vdd_level(core->vdd_class, level);
}
/*
* Remove vote for a voltage level corresponding to a clock's rate.
*/
static void clk_unvote_rate_vdd(struct clk_core *core, unsigned long rate)
{
int level;
if (!core->vdd_class)
return;
level = clk_find_vdd_level(core, rate);
if (level < 0)
return;
clk_unvote_vdd_level(core->vdd_class, level);
}
static bool clk_is_rate_level_valid(struct clk_core *core, unsigned long rate)
{
int level;
if (!core->vdd_class)
return true;
level = clk_find_vdd_level(core, rate);
return level >= 0;
}
static int clk_vdd_class_init(struct clk_vdd_class *vdd)
{
struct clk_handoff_vdd *v;
if (vdd->skip_handoff)
return 0;
list_for_each_entry(v, &clk_handoff_vdd_list, list) {
if (v->vdd_class == vdd)
return 0;
}
pr_debug("voting for vdd_class %s\n", vdd->class_name);
if (clk_vote_vdd_level(vdd, vdd->num_levels - 1))
pr_err("failed to vote for %s\n", vdd->class_name);
v = kmalloc(sizeof(*v), GFP_KERNEL);
if (!v)
return -ENOMEM;
v->vdd_class = vdd;
list_add_tail(&v->list, &clk_handoff_vdd_list);
return 0;
}
/*** clk api ***/
static void clk_core_rate_unprotect(struct clk_core *core)
@ -727,6 +958,9 @@ static void clk_core_unprepare(struct clk_core *core)
clk_pm_runtime_put(core);
trace_clk_unprepare_complete(core);
clk_unvote_rate_vdd(core, core->rate);
clk_core_unprepare(core->parent);
}
@ -777,13 +1011,21 @@ static int clk_core_prepare(struct clk_core *core)
trace_clk_prepare(core);
ret = clk_vote_rate_vdd(core, core->rate);
if (ret) {
clk_core_unprepare(core->parent);
return ret;
}
if (core->ops->prepare)
ret = core->ops->prepare(core->hw);
trace_clk_prepare_complete(core);
if (ret)
if (ret) {
clk_unvote_rate_vdd(core, core->rate);
goto unprepare;
}
}
core->prepare_count++;
@ -1073,6 +1315,7 @@ __setup("clk_ignore_unused", clk_ignore_unused_setup);
static int clk_disable_unused(void)
{
struct clk_core *core;
struct clk_handoff_vdd *v, *v_temp;
if (clk_ignore_unused) {
pr_warn("clk: Not disabling unused clocks\n");
@ -1093,6 +1336,13 @@ static int clk_disable_unused(void)
hlist_for_each_entry(core, &clk_orphan_list, child_node)
clk_unprepare_unused_subtree(core);
list_for_each_entry_safe(v, v_temp, &clk_handoff_vdd_list, list) {
clk_unvote_vdd_level(v->vdd_class,
v->vdd_class->num_levels - 1);
list_del(&v->list);
kfree(v);
}
clk_prepare_unlock();
return 0;
@ -1724,11 +1974,20 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
}
}
/*
* Certain PLLs only have 16 bits to program the fractional divider.
* Hence the programmed rate might be slightly different than the
* requested one.
*/
if ((core->flags & CLK_SET_RATE_PARENT) && parent &&
best_parent_rate != parent->rate)
(DIV_ROUND_CLOSEST(best_parent_rate, 1000) !=
DIV_ROUND_CLOSEST(parent->rate, 1000)))
top = clk_calc_new_rates(parent, best_parent_rate);
out:
if (!clk_is_rate_level_valid(core, rate))
return NULL;
clk_calc_subtree(core, new_rate, parent, p_index);
return top;
@ -1811,6 +2070,15 @@ static int clk_change_rate(struct clk_core *core)
clk_enable_unlock(flags);
}
trace_clk_set_rate(core, core->new_rate);
/* Enforce vdd requirements for new frequency. */
if (core->prepare_count) {
rc = clk_vote_rate_vdd(core, core->new_rate);
if (rc)
goto out;
}
if (core->new_parent && core->new_parent != core->parent) {
old_parent = __clk_set_parent_before(core, core->new_parent);
trace_clk_set_parent(core, core->new_parent);
@ -1831,19 +2099,21 @@ static int clk_change_rate(struct clk_core *core)
if (core->flags & CLK_OPS_PARENT_ENABLE)
clk_core_prepare_enable(parent);
trace_clk_set_rate(core, core->new_rate);
if (!skip_set_rate && core->ops->set_rate) {
rc = core->ops->set_rate(core->hw, core->new_rate,
best_parent_rate);
if (rc) {
trace_clk_set_rate_complete(core, core->new_rate);
goto out;
goto err_set_rate;
}
}
trace_clk_set_rate_complete(core, core->new_rate);
/* Release vdd requirements for old frequency. */
if (core->prepare_count)
clk_unvote_rate_vdd(core, old_rate);
core->rate = clk_recalc(core, best_parent_rate);
if (core->flags & CLK_SET_RATE_UNGATE) {
@ -1874,13 +2144,19 @@ static int clk_change_rate(struct clk_core *core)
continue;
rc = clk_change_rate(child);
if (rc)
goto out;
goto err_set_rate;
}
/* handle the new child who might not be in core->children yet */
if (core->new_child)
rc = clk_change_rate(core->new_child);
clk_pm_runtime_put(core);
return rc;
err_set_rate:
if (core->prepare_count)
clk_unvote_rate_vdd(core, core->new_rate);
out:
clk_pm_runtime_put(core);
return rc;
@ -3236,8 +3512,19 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
core->num_parents = hw->init->num_parents;
core->min_rate = 0;
core->max_rate = ULONG_MAX;
core->vdd_class = hw->init->vdd_class;
core->rate_max = hw->init->rate_max;
core->num_rate_max = hw->init->num_rate_max;
hw->core = core;
if (core->vdd_class) {
ret = clk_vdd_class_init(core->vdd_class);
if (ret) {
pr_err("Failed to initialize vdd class\n");
goto fail_parent_names;
}
}
/* allocate local copy in case parent_names is __initdata */
core->parent_names = kcalloc(core->num_parents, sizeof(char *),
GFP_KERNEL);

View File

@ -14,6 +14,7 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_clk.h>
#include <linux/mutex.h>
#ifdef CONFIG_COMMON_CLK
@ -260,6 +261,9 @@ struct clk_ops {
* @parent_names: array of string names for all possible parents
* @num_parents: number of possible parents
* @flags: framework-level hints and quirks
* @vdd_class: voltage scaling requirement class
* @rate_max: maximum clock rate in Hz supported at each voltage level
* @num_rate_max: number of maximum voltage level supported
*/
struct clk_init_data {
const char *name;
@ -267,8 +271,73 @@ struct clk_init_data {
const char * const *parent_names;
u8 num_parents;
unsigned long flags;
struct clk_vdd_class *vdd_class;
unsigned long *rate_max;
int num_rate_max;
};
struct regulator;
/**
* struct clk_vdd_class - Voltage scaling class
* @class_name: name of the class
* @regulator: array of regulators
* @num_regulators: size of regulator array. Standard regulator APIs will be
used if this field > 0
* @set_vdd: function to call when applying a new voltage setting
* @vdd_uv: sorted 2D array of legal voltage settings. Indexed by level, then
regulator
* @level_votes: array of votes for each level
* @num_levels: specifies the size of level_votes array
* @skip_handoff: do not vote for the max possible voltage during init
* @use_max_uV: use INT_MAX for max_uV when calling regulator_set_voltage
* @cur_level: the currently set voltage level
* @lock: lock to protect this struct
*/
struct clk_vdd_class {
const char *class_name;
struct regulator **regulator;
int num_regulators;
int (*set_vdd)(struct clk_vdd_class *v_class, int level);
int *vdd_uv;
int *level_votes;
int num_levels;
bool skip_handoff;
bool use_max_uV;
unsigned long cur_level;
struct mutex lock;
};
#define DEFINE_VDD_CLASS(_name, _set_vdd, _num_levels) \
struct clk_vdd_class _name = { \
.class_name = #_name, \
.set_vdd = _set_vdd, \
.level_votes = (int [_num_levels]) {}, \
.num_levels = _num_levels, \
.cur_level = _num_levels, \
.lock = __MUTEX_INITIALIZER(_name.lock) \
}
#define DEFINE_VDD_REGULATORS(_name, _num_levels, _num_regulators, _vdd_uv) \
struct clk_vdd_class _name = { \
.class_name = #_name, \
.vdd_uv = _vdd_uv, \
.regulator = (struct regulator * [_num_regulators]) {}, \
.num_regulators = _num_regulators, \
.level_votes = (int [_num_levels]) {}, \
.num_levels = _num_levels, \
.cur_level = _num_levels, \
.lock = __MUTEX_INITIALIZER(_name.lock) \
}
#define DEFINE_VDD_REGS_INIT(_name, _num_regulators) \
struct clk_vdd_class _name = { \
.class_name = #_name, \
.regulator = (struct regulator * [_num_regulators]) {}, \
.num_regulators = _num_regulators, \
.lock = __MUTEX_INITIALIZER(_name.lock) \
}
/**
* struct clk_hw - handle for traversing from a struct clk to its corresponding
* hardware-specific structure. struct clk_hw should be declared within struct