summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2012-12-07 15:58:59 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:46:09 -0700
commit6626ec4a5c3f0fdad2520733b9d7492dd08de807 (patch)
tree13b6232502481b5c587875ab69722dcb09c7a2ca /arch
parent5f3404e716d5773701a51b6ddfe016efc22a8c35 (diff)
ARM: tegra11: clock: Use tabulated EMC clock register
Instead of constructing settings for EMC clock source/divider register, use value specified in the EMC DFS table. Bug 1188643 Change-Id: I4d28ed00c0b049d4ab5ad645cbf721ef6453be8b Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/169556 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-tegra/tegra11_emc.c106
1 files changed, 54 insertions, 52 deletions
diff --git a/arch/arm/mach-tegra/tegra11_emc.c b/arch/arm/mach-tegra/tegra11_emc.c
index b6d56f8a725c..40b786ea5d4d 100644
--- a/arch/arm/mach-tegra/tegra11_emc.c
+++ b/arch/arm/mach-tegra/tegra11_emc.c
@@ -58,10 +58,9 @@ enum {
};
#define EMC_CLK_DIV_SHIFT 0
-#define EMC_CLK_DIV_MAX_VALUE 0xFF
#define EMC_CLK_DIV_MASK (0xFF << EMC_CLK_DIV_SHIFT)
#define EMC_CLK_SOURCE_SHIFT 29
-#define EMC_CLK_SOURCE_MAX_VALUE 3
+#define EMC_CLK_SOURCE_MASK (0x7 << EMC_CLK_SOURCE_SHIFT)
#define EMC_CLK_LOW_JITTER_ENABLE (0x1 << 31)
#define EMC_CLK_MC_SAME_FREQ (0x1 << 16)
@@ -850,71 +849,74 @@ bool tegra_emc_is_parent_ready(unsigned long rate, struct clk **parent,
return false;
}
-/* FIXME: take advantage of table->src_sel_reg */
-static int find_matching_input(const struct tegra11_emc_table *table,
- struct clk *pll_c, struct emc_sel *emc_clk_sel)
+static inline const struct clk_mux_sel *get_emc_input(u32 val)
{
- u32 div_value = 0;
- unsigned long input_rate = 0;
- unsigned long table_rate = table->rate * 1000; /* table rate in kHz */
- struct clk *src = tegra_get_clock_by_name(table->src_name);
const struct clk_mux_sel *sel;
for (sel = emc->inputs; sel->input != NULL; sel++) {
- if (sel->input != src)
- continue;
- /*
- * PLLC is a scalable source. For rates below PLL_C_DIRECT_FLOOR
- * configure PLLC at double rate and set 1:2 divider, otherwise
- * configure PLLC at target rate with divider 1:1.
- */
- if (src == pll_c) {
-#ifdef CONFIG_TEGRA_DUAL_CBUS
- if (table_rate < PLL_C_DIRECT_FLOOR) {
- input_rate = 2 * table_rate;
- div_value = 2;
- } else {
- input_rate = table_rate;
- div_value = 0;
- }
+ if (sel->value == val)
break;
-#else
- continue; /* pll_c is used for cbus - skip */
-#endif
- }
+ }
+ return sel;
+}
- /*
- * All other clock sources are fixed rate sources, and must
- * run at rate that is an exact multiple of the target.
- */
- input_rate = clk_get_rate(src);
+static int find_matching_input(const struct tegra11_emc_table *table,
+ struct clk *pll_c, struct emc_sel *emc_clk_sel)
+{
+ u32 div_value = (table->src_sel_reg & EMC_CLK_DIV_MASK) >>
+ EMC_CLK_DIV_SHIFT;
+ u32 src_value = (table->src_sel_reg & EMC_CLK_SOURCE_MASK) >>
+ EMC_CLK_SOURCE_SHIFT;
+ unsigned long input_rate = 0;
+ unsigned long table_rate = table->rate * 1000; /* table rate in kHz */
+ const struct clk_mux_sel *sel = get_emc_input(src_value);
- if ((input_rate >= table_rate) &&
- (input_rate % table_rate == 0)) {
- div_value = 2 * input_rate / table_rate - 2;
- break;
- }
+ if (div_value & 0x1) {
+ pr_warn("tegra: invalid odd divider for EMC rate %lu\n",
+ table_rate);
+ return -EINVAL;
}
-
- if (!sel->input || (sel->value > EMC_CLK_SOURCE_MAX_VALUE) ||
- (div_value > EMC_CLK_DIV_MAX_VALUE)) {
+ if (!sel->input) {
pr_warn("tegra: no matching input found for EMC rate %lu\n",
table_rate);
return -EINVAL;
}
+ if (div_value && (table->src_sel_reg & EMC_CLK_LOW_JITTER_ENABLE)) {
+ pr_warn("tegra: invalid LJ path for EMC rate %lu\n",
+ table_rate);
+ return -EINVAL;
+ }
+ if (!(table->src_sel_reg & EMC_CLK_MC_SAME_FREQ) !=
+ !(MC_EMEM_ARB_MISC0_EMC_SAME_FREQ &
+ table->burst_regs[MC_EMEM_ARB_MISC0_INDEX])) {
+ pr_warn("tegra: ambiguous EMC to MC ratio for EMC rate %lu\n",
+ table_rate);
+ return -EINVAL;
+ }
- emc_clk_sel->input = sel->input;
- emc_clk_sel->input_rate = input_rate;
+ if (sel->input == pll_c) {
+ /* PLLC is a scalable source */
+#ifdef CONFIG_TEGRA_DUAL_CBUS
+ input_rate = table_rate * (1 + div_value / 2);
+#else
+ pr_warn("tegra: %s cannot be used as EMC rate %lu source\n",
+ sel->input->name, table_rate);
+ return -EINVAL;
+#endif
+ } else {
+ /* all other sources are fixed, must exactly match the rate */
+ input_rate = clk_get_rate(sel->input);
+ if (input_rate != (table_rate * (1 + div_value / 2))) {
+ pr_warn("tegra: %s rate %lu does not match EMC rate %lu\n",
+ sel->input->name, input_rate, table_rate);
+ return -EINVAL;
+ }
+ }
/* Get ready emc clock selection settings for this table rate */
- emc_clk_sel->value = sel->value << EMC_CLK_SOURCE_SHIFT;
- emc_clk_sel->value |= (div_value << EMC_CLK_DIV_SHIFT);
- if ((div_value == 0) && (emc_clk_sel->input == emc->parent))
- emc_clk_sel->value |= EMC_CLK_LOW_JITTER_ENABLE;
-
- if (MC_EMEM_ARB_MISC0_EMC_SAME_FREQ &
- table->burst_regs[MC_EMEM_ARB_MISC0_INDEX])
- emc_clk_sel->value |= EMC_CLK_MC_SAME_FREQ;
+ emc_clk_sel->input = sel->input;
+ emc_clk_sel->input_rate = input_rate;
+ emc_clk_sel->value = table->src_sel_reg;
return 0;
}