summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra11_clocks.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2012-08-26 03:20:08 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:32:56 -0700
commit64d6c261efd17c7adb1c18363bd5e2a4f1058d4d (patch)
tree6bb00e42d2c75ea2fb9e3a1df0936dd415b45663 /arch/arm/mach-tegra/tegra11_clocks.c
parent7458c3413e48268dcd08365e234172807d7259a0 (diff)
ARM: tegra11: clock: Construct cpufreq table
Dynamically constructed cpufreq table based on CPU dvfs tables for the particular chip (rather than use pre-populated tables that can only approximate dvfs ladders for different chips within SKU range). Change-Id: Ie86cae192000fa105f9ab10b8f370b2d8b78b521 Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/127376 Reviewed-by: Satya Popuri <spopuri@nvidia.com> Reviewed-by: Bo Yan <byan@nvidia.com> Rebase-Id: R3ad93ccdf3ee73af00bf72f8155c266347ff620e
Diffstat (limited to 'arch/arm/mach-tegra/tegra11_clocks.c')
-rw-r--r--arch/arm/mach-tegra/tegra11_clocks.c220
1 files changed, 101 insertions, 119 deletions
diff --git a/arch/arm/mach-tegra/tegra11_clocks.c b/arch/arm/mach-tegra/tegra11_clocks.c
index f8a071f31e46..3f5245c78c81 100644
--- a/arch/arm/mach-tegra/tegra11_clocks.c
+++ b/arch/arm/mach-tegra/tegra11_clocks.c
@@ -6416,134 +6416,116 @@ void __init tegra11x_init_clocks(void)
* Frequency table index must be sequential starting at 0 and frequencies
* must be ascending.
*/
+#define CPU_FREQ_STEP 100000 /* 100MHz */
+#define CPU_FREQ_TABLE_MAX_SIZE (2 * MAX_DVFS_FREQS + 1)
-static struct cpufreq_frequency_table freq_table_300MHz[] = {
- { 0, 204000 },
- { 1, 300000 },
- { 2, CPUFREQ_TABLE_END },
-};
-
-static struct cpufreq_frequency_table freq_table_1p0GHz[] = {
- { 0, 102000 },
- { 1, 204000 },
- { 2, 312000 },
- { 3, 456000 },
- { 4, 608000 },
- { 5, 760000 },
- { 6, 816000 },
- { 7, 912000 },
- { 8, 1000000 },
- { 9, CPUFREQ_TABLE_END },
-};
-
-static struct cpufreq_frequency_table freq_table_1p3GHz[] = {
- { 0, 102000 },
- { 1, 204000 },
- { 2, 340000 },
- { 3, 475000 },
- { 4, 640000 },
- { 5, 760000 },
- { 6, 880000 },
- { 7, 1000000 },
- { 8, 1100000 },
- { 9, 1200000 },
- {10, 1300000 },
- {11, CPUFREQ_TABLE_END },
-};
-
-static struct cpufreq_frequency_table freq_table_1p4GHz[] = {
- { 0, 102000 },
- { 1, 204000 },
- { 2, 370000 },
- { 3, 475000 },
- { 4, 620000 },
- { 5, 760000 },
- { 6, 880000 },
- { 7, 1000000 },
- { 8, 1100000 },
- { 9, 1200000 },
- {10, 1300000 },
- {11, 1400000 },
- {12, CPUFREQ_TABLE_END },
-};
-
-static struct tegra_cpufreq_table_data cpufreq_tables[] = {
- { freq_table_300MHz, 0, 1 },
- { freq_table_1p0GHz, 2, 7, 2},
- { freq_table_1p3GHz, 2, 9, 2},
- { freq_table_1p4GHz, 2, 10, 2},
-};
-
-static int clip_cpu_rate_limits(
- struct cpufreq_frequency_table *freq_table,
- struct cpufreq_policy *policy,
- struct clk *cpu_clk_g,
- struct clk *cpu_clk_lp)
-{
- 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,
- cpu_clk_lp->max_rate / 1000, CPUFREQ_RELATION_H, &idx);
- 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");
- 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;
-}
+static struct cpufreq_frequency_table freq_table[CPU_FREQ_TABLE_MAX_SIZE];
+static struct tegra_cpufreq_table_data freq_table_data;
struct tegra_cpufreq_table_data *tegra_cpufreq_table_get(void)
{
- int i, ret;
- unsigned long selection_rate;
+ int i, j;
+ unsigned int freq, lp_backup_freq, g_vmin_freq, g_start_freq, max_freq;
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);
- 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];
- }
+ /* Initialize once */
+ if (freq_table_data.freq_table)
+ return &freq_table_data;
+
+ /* Clean table */
+ for (i = 0; i < CPU_FREQ_TABLE_MAX_SIZE; i++) {
+ freq_table[i].index = i;
+ freq_table[i].frequency = CPUFREQ_TABLE_END;
+ }
+
+ lp_backup_freq = cpu_clk_lp->u.cpu.backup_rate / 1000;
+ if (!lp_backup_freq) {
+ WARN(1, "%s: cannot make cpufreq table: no LP CPU backup rate\n",
+ __func__);
+ return NULL;
+ }
+ if (!cpu_clk_lp->dvfs) {
+ WARN(1, "%s: cannot make cpufreq table: no LP CPU dvfs\n",
+ __func__);
+ return NULL;
+ }
+ if (!cpu_clk_g->dvfs) {
+ WARN(1, "%s: cannot make cpufreq table: no G CPU dvfs\n",
+ __func__);
+ return NULL;
+ }
+ g_vmin_freq = cpu_clk_g->dvfs->freqs[0] / 1000;
+ if (g_vmin_freq <= lp_backup_freq) {
+ WARN(1, "%s: cannot make cpufreq table: LP CPU backup rate"
+ " exceeds G CPU rate at Vmin\n", __func__);
+ return NULL;
+ }
+
+ /* Start with backup frequencies */
+ i = 0;
+ freq = lp_backup_freq;
+ freq_table[i++].frequency = freq/4;
+ freq_table[i++].frequency = freq/2;
+ freq_table[i++].frequency = freq;
+
+ /* Throttle low index at backup level*/
+ freq_table_data.throttle_lowest_index = i - 1;
+
+ /*
+ * Next, set table steps along LP CPU dvfs ladder, but make sure G CPU
+ * dvfs rate at minimum voltage is not missed (if it happens to be below
+ * LP maximum rate)
+ */
+ max_freq = cpu_clk_lp->max_rate / 1000;
+ for (j = 0; j < cpu_clk_lp->dvfs->num_freqs; j++) {
+ freq = cpu_clk_lp->dvfs->freqs[j] / 1000;
+ if (freq <= lp_backup_freq)
+ continue;
+
+ if (freq >= g_vmin_freq) {
+ freq_table[i++].frequency = g_vmin_freq;
+ if (freq == g_vmin_freq)
+ continue;
+ }
+ freq_table[i++].frequency = freq;
+
+ if (freq == max_freq)
+ break;
+ }
+
+ /* Set G CPU min rate at least one table step below LP maximum */
+ cpu_clk_g->min_rate = min(freq_table[i-2].frequency, g_vmin_freq)*1000;
+
+ /* Suspend index at max LP CPU */
+ freq_table_data.suspend_index = i - 1;
+
+ /* Fill in "hole" (if any) between LP CPU maximum rate and G CPU dvfs
+ ladder rate at minimum voltage */
+ if (freq < g_vmin_freq) {
+ int n = (g_vmin_freq - freq) / CPU_FREQ_STEP;
+ for (j = 0; j <= n; j++) {
+ freq = g_vmin_freq - CPU_FREQ_STEP * (n - j);
+ freq_table[i++].frequency = freq;
}
}
- WARN(1, "%s: No cpufreq table matching G & LP cpu ranges", __func__);
- return NULL;
+
+ /* Now, step along the rest of G CPU dvfs ladder */
+ g_start_freq = freq;
+ max_freq = cpu_clk_g->max_rate / 1000;
+ for (j = 0; j < cpu_clk_g->dvfs->num_freqs; j++) {
+ freq = cpu_clk_g->dvfs->freqs[j] / 1000;
+ if (freq > g_start_freq)
+ freq_table[i++].frequency = freq;
+ if (freq == max_freq)
+ break;
+ }
+
+ /* Throttle high index one step below maximum */
+ BUG_ON(i >= CPU_FREQ_TABLE_MAX_SIZE);
+ freq_table_data.throttle_highest_index = i - 2;
+ freq_table_data.freq_table = freq_table;
+ return &freq_table_data;
}
unsigned long tegra_emc_to_cpu_ratio(unsigned long cpu_rate)