summaryrefslogtreecommitdiff
path: root/arch/arm
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2011-06-18 00:35:46 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:47:14 -0800
commit5aa5d0a98a577476ae655fe3c3bdc87432befa9b (patch)
tree5cc02ef4902da16e5a874eb80537960c3f9e0dce /arch/arm
parent881fb222cc9cb964103e494bd42505d93102f01a (diff)
ARM: tegra: dvfs: Update Tegra3 cpufreq table selection
- For selection of cpufreq scaling table used top-most rate in G CPU dvfs table, instead of G CPU max rate. Commonly the above rates are the same, however, in case when PMU limitations on core voltage indirectly (VDD_CPU on VDD_CORE dependency) lower cpu max rate, the top-most dvfs rate should be used for table selection, and the max rate clipped to table entry. - Replaced BUGs in table selection implementation with errors. Thus, when no table is found cpufreq is not installed, but the system boots with respective error messages. - Step up suspend frequency index in cpufreq tables to reduce suspend entry latency (the selected rate is still low enough to work under Vmin voltage setting). Original-Change-Id: I45db19dbf5b48cef80db35663db2df3b68473993 Reviewed-on: http://git-master/r/37415 Reviewed-by: Varun Colbert <vcolbert@nvidia.com> Tested-by: Varun Colbert <vcolbert@nvidia.com> Rebase-Id: R59fb213db14d868bec0ca701e1c73dd9d1918e82
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.c4
-rw-r--r--arch/arm/mach-tegra/tegra3_clocks.c54
2 files changed, 44 insertions, 14 deletions
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index 8280d7e9e76b..d032205e1146 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -581,7 +581,9 @@ static int __init tegra_cpufreq_init(void)
struct tegra_cpufreq_table_data *table_data =
tegra_cpufreq_table_get();
- BUG_ON(!table_data);
+ if (IS_ERR_OR_NULL(table_data))
+ return -EINVAL;
+
suspend_index = table_data->suspend_index;
#ifdef CONFIG_TEGRA_THERMAL_THROTTLE
diff --git a/arch/arm/mach-tegra/tegra3_clocks.c b/arch/arm/mach-tegra/tegra3_clocks.c
index 062af82ac204..49fc77c48409 100644
--- a/arch/arm/mach-tegra/tegra3_clocks.c
+++ b/arch/arm/mach-tegra/tegra3_clocks.c
@@ -4054,12 +4054,12 @@ static struct cpufreq_frequency_table freq_table_1p4GHz[] = {
static struct tegra_cpufreq_table_data cpufreq_tables[] = {
{ freq_table_300MHz, 0, 1 },
- { freq_table_1p0GHz, 2, 7 },
- { freq_table_1p3GHz, 2, 9, 1},
- { freq_table_1p4GHz, 2, 10, 1},
+ { freq_table_1p0GHz, 2, 7, 2},
+ { freq_table_1p3GHz, 2, 9, 2},
+ { freq_table_1p4GHz, 2, 10, 2},
};
-static void clip_cpu_rate_limits(
+static int clip_cpu_rate_limits(
struct cpufreq_frequency_table *freq_table,
struct cpufreq_policy *policy,
struct clk *cpu_clk_g,
@@ -4067,6 +4067,21 @@ static void clip_cpu_rate_limits(
{
int idx, ret;
+ /* clip CPU G mode maximum frequency to table entry */
+ ret = cpufreq_frequency_table_target(policy, freq_table,
+ cpu_clk_g->max_rate / 1000, CPUFREQ_RELATION_H, &idx);
+ if (ret) {
+ pr_err("%s: G CPU max rate %lu outside of cpufreq table",
+ __func__, cpu_clk_g->max_rate);
+ return ret;
+ }
+ cpu_clk_g->max_rate = freq_table[idx].frequency * 1000;
+ if (cpu_clk_g->max_rate < cpu_clk_lp->max_rate) {
+ pr_err("%s: G CPU max rate %lu is below LP CPU max rate %lu",
+ __func__, cpu_clk_g->max_rate, cpu_clk_lp->max_rate);
+ return -EINVAL;
+ }
+
/* clip CPU LP mode maximum frequency to table entry, and
set CPU G mode minimum frequency one table step below */
ret = cpufreq_frequency_table_target(policy, freq_table,
@@ -4074,33 +4089,46 @@ static void clip_cpu_rate_limits(
if (ret || !idx) {
pr_err("%s: LP CPU max rate %lu %s of cpufreq table", __func__,
cpu_clk_lp->max_rate, ret ? "outside" : "at the bottom");
- BUG();
+ return ret;
}
cpu_clk_lp->max_rate = freq_table[idx].frequency * 1000;
cpu_clk_g->min_rate = freq_table[idx-1].frequency * 1000;
+ return 0;
}
struct tegra_cpufreq_table_data *tegra_cpufreq_table_get(void)
{
int i, ret;
+ unsigned long selection_rate;
struct clk *cpu_clk_g = tegra_get_clock_by_name("cpu_g");
struct clk *cpu_clk_lp = tegra_get_clock_by_name("cpu_lp");
+ /* For table selection use top cpu_g rate in dvfs ladder; selection
+ rate may exceed cpu max_rate (e.g., because of edp limitations on
+ cpu voltage) - in any case max_rate will be clipped to the table */
+ if (cpu_clk_g->dvfs && cpu_clk_g->dvfs->num_freqs)
+ selection_rate =
+ cpu_clk_g->dvfs->freqs[cpu_clk_g->dvfs->num_freqs - 1];
+ else
+ selection_rate = cpu_clk_g->max_rate;
+
for (i = 0; i < ARRAY_SIZE(cpufreq_tables); i++) {
struct cpufreq_policy policy;
policy.cpu = 0; /* any on-line cpu */
ret = cpufreq_frequency_table_cpuinfo(
&policy, cpufreq_tables[i].freq_table);
- BUG_ON(ret);
- if ((policy.max * 1000) == cpu_clk_g->max_rate) {
- clip_cpu_rate_limits(cpufreq_tables[i].freq_table,
- &policy, cpu_clk_g, cpu_clk_lp);
- return &cpufreq_tables[i];
+ if (!ret) {
+ if ((policy.max * 1000) == selection_rate) {
+ ret = clip_cpu_rate_limits(
+ cpufreq_tables[i].freq_table,
+ &policy, cpu_clk_g, cpu_clk_lp);
+ if (!ret)
+ return &cpufreq_tables[i];
+ }
}
}
- pr_err("%s: No cpufreq table matching cpu range", __func__);
- BUG();
- return &cpufreq_tables[0];
+ WARN(1, "%s: No cpufreq table matching G & LP cpu ranges", __func__);
+ return NULL;
}
unsigned long tegra_emc_to_cpu_ratio(unsigned long cpu_rate)