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:
parent
8ff1a590c2
commit
f349485712
10 changed files with 370 additions and 14 deletions
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue