diff options
author | Colin Cross <ccross@android.com> | 2010-11-01 17:27:29 -0700 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2010-11-03 17:55:53 -0700 |
commit | a1d72a522d5ecc98f6b57816f90141d1f0eecfeb (patch) | |
tree | 359602ade94080dd4181590068f5eca9b9b49cf8 /arch | |
parent | c95714d0ba915e422ad676f68630fcd513f34295 (diff) |
ARM: tegra: dvfs: Get rid of dvfs_lock and move init later
Get rid of dvfs_lock, replacing it with the cansleep flag on clocks.
Clocks with the cansleep flag set will lock a mutex before calling
into dvfs.
Also does the regulator api calls during late init, after the
regulators have been probed.
Signed-off-by: Colin Cross <ccross@android.com>
Change-Id: I5b8bd249bd4f3ae495f2076f1e6d2bfb38737f29
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-tegra/clock.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/dvfs.c | 84 | ||||
-rw-r--r-- | arch/arm/mach-tegra/dvfs.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra2_dvfs.c | 3 |
4 files changed, 49 insertions, 43 deletions
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index 4486214c52f5..34c2c29fa760 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -568,6 +568,7 @@ int __init tegra_disable_boot_clocks(void) int __init tegra_late_init_clock(void) { + tegra_dvfs_late_init(); tegra_disable_boot_clocks(); tegra_clk_set_dvfs_rates(); return 0; diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c index 0a2135e3b784..d29315aed0dc 100644 --- a/arch/arm/mach-tegra/dvfs.c +++ b/arch/arm/mach-tegra/dvfs.c @@ -42,21 +42,11 @@ struct dvfs_reg { int millivolts; }; -static LIST_HEAD(dvfs_list); static LIST_HEAD(dvfs_debug_list); static LIST_HEAD(dvfs_reg_list); -static DEFINE_MUTEX(dvfs_lock); - -void lock_dvfs(void) -{ - mutex_lock(&dvfs_lock); -} - -void unlock_dvfs(void) -{ - mutex_unlock(&dvfs_lock); -} +static DEFINE_MUTEX(dvfs_debug_list_lock); +static DEFINE_MUTEX(dvfs_reg_list_lock); static int dvfs_reg_set_voltage(struct dvfs_reg *dvfs_reg) { @@ -71,46 +61,53 @@ static int dvfs_reg_set_voltage(struct dvfs_reg *dvfs_reg) dvfs_reg->millivolts = millivolts; + if (!dvfs_reg->reg) { + pr_warn("dvfs set voltage on %s ignored\n", dvfs_reg->reg_id); + return 0; + } + return regulator_set_voltage(dvfs_reg->reg, millivolts * 1000, dvfs_reg->max_millivolts * 1000); } -static int dvfs_reg_get_voltage(struct dvfs_reg *dvfs_reg) +static int dvfs_reg_connect_to_regulator(struct dvfs_reg *dvfs_reg) { - int ret = regulator_get_voltage(dvfs_reg->reg); + struct regulator *reg; - if (ret > 0) - return ret / 1000; + if (!dvfs_reg->reg) { + reg = regulator_get(NULL, dvfs_reg->reg_id); + if (IS_ERR(reg)) + return -EINVAL; + } - return ret; + dvfs_reg->reg = reg; + + return 0; } static struct dvfs_reg *get_dvfs_reg(struct dvfs *d) { struct dvfs_reg *dvfs_reg; - struct regulator *reg; + + mutex_lock(&dvfs_reg_list_lock); list_for_each_entry(dvfs_reg, &dvfs_reg_list, node) if (!strcmp(d->reg_id, dvfs_reg->reg_id)) - return dvfs_reg; - - reg = regulator_get(NULL, d->reg_id); - if (IS_ERR(reg)) - return NULL; + goto out; dvfs_reg = kzalloc(sizeof(struct dvfs_reg), GFP_KERNEL); if (!dvfs_reg) { pr_err("%s: Failed to allocate dvfs_reg\n", __func__); - regulator_put(reg); - return NULL; + goto out; } INIT_LIST_HEAD(&dvfs_reg->dvfs); - dvfs_reg->reg = reg; dvfs_reg->reg_id = kstrdup(d->reg_id, GFP_KERNEL); list_add_tail(&dvfs_reg->node, &dvfs_reg_list); +out: + mutex_unlock(&dvfs_reg_list_lock); return dvfs_reg; } @@ -127,7 +124,7 @@ static struct dvfs_reg *attach_dvfs_reg(struct dvfs *d) if (d->max_millivolts > d->dvfs_reg->max_millivolts) d->dvfs_reg->max_millivolts = d->max_millivolts; - d->cur_millivolts = dvfs_reg_get_voltage(d->dvfs_reg); + d->cur_millivolts = d->max_millivolts; return dvfs_reg; } @@ -177,7 +174,7 @@ int tegra_dvfs_set_rate(struct clk *c, unsigned long rate) c->dvfs_rate = rate; - freq_up = (c->refcnt == 0) || (rate > c->rate); + freq_up = (c->refcnt == 0) || (rate > clk_get_rate_locked(c)); list_for_each_entry(d, &c->dvfs, node) { if (d->higher == freq_up) @@ -197,7 +194,8 @@ int tegra_dvfs_set_rate(struct clk *c, unsigned long rate) } EXPORT_SYMBOL(tegra_dvfs_set_rate); -int tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d) +/* May only be called during clock init, does not take any locks on clock c. */ +int __init tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d) { int i; struct dvfs_reg *dvfs_reg; @@ -221,30 +219,38 @@ int tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d) } d->num_freqs = i; - if (d->auto_dvfs) + if (d->auto_dvfs) { c->auto_dvfs = true; + clk_set_cansleep(c); + } c->is_dvfs = true; - smp_wmb(); list_add_tail(&d->node, &c->dvfs); + mutex_lock(&dvfs_debug_list_lock); list_add_tail(&d->debug_node, &dvfs_debug_list); + mutex_unlock(&dvfs_debug_list_lock); return 0; } -int __init tegra_init_dvfs(void) +/* + * Iterate through all the dvfs regulators, finding the regulator exported + * by the regulator api for each one. Must be called in late init, after + * all the regulator api's regulators are initialized. + */ +int __init tegra_dvfs_late_init(void) { - lock_dvfs(); - tegra2_init_dvfs(); + struct dvfs_reg *dvfs_reg; - tegra_clk_set_dvfs_rates(); - unlock_dvfs(); + mutex_lock(&dvfs_reg_list_lock); + list_for_each_entry(dvfs_reg, &dvfs_reg_list, node) + dvfs_reg_connect_to_regulator(dvfs_reg); + mutex_unlock(&dvfs_reg_list_lock); return 0; } -late_initcall(tegra_init_dvfs); #ifdef CONFIG_DEBUG_FS static int dvfs_tree_sort_cmp(void *p, struct list_head *a, struct list_head *b) @@ -273,7 +279,7 @@ static int dvfs_tree_show(struct seq_file *s, void *data) seq_printf(s, " clock rate mV\n"); seq_printf(s, "--------------------------------\n"); - lock_dvfs(); + mutex_lock(&dvfs_debug_list_lock); list_sort(NULL, &dvfs_debug_list, dvfs_tree_sort_cmp); @@ -288,7 +294,7 @@ static int dvfs_tree_show(struct seq_file *s, void *data) d->cur_rate, d->cur_millivolts); } - unlock_dvfs(); + mutex_unlock(&dvfs_debug_list_lock); return 0; } diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h index df6a3866d31b..e5eac6cf9cd0 100644 --- a/arch/arm/mach-tegra/dvfs.h +++ b/arch/arm/mach-tegra/dvfs.h @@ -49,11 +49,9 @@ struct dvfs { struct list_head reg_node; }; -void lock_dvfs(void); -void unlock_dvfs(void); - void tegra2_init_dvfs(void); int tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d); int dvfs_debugfs_init(struct dentry *clk_debugfs_root); +int tegra_dvfs_late_init(void); #endif diff --git a/arch/arm/mach-tegra/tegra2_dvfs.c b/arch/arm/mach-tegra/tegra2_dvfs.c index 024e1b970638..4a831435991e 100644 --- a/arch/arm/mach-tegra/tegra2_dvfs.c +++ b/arch/arm/mach-tegra/tegra2_dvfs.c @@ -18,6 +18,7 @@ */ #include <linux/kernel.h> +#include <linux/init.h> #include <linux/string.h> #include "clock.h" @@ -132,7 +133,7 @@ static struct dvfs dvfs_init[] = { CORE_DVFS("NVRM_DEVID_CLK_SRC", 1, MHZ, 480, 600, 800, 1067, 1067), }; -void tegra2_init_dvfs(void) +void __init tegra2_init_dvfs(void) { int i; struct clk *c; |