Upon reset, the CRC_OSC_CTRL register defaults to a 13 MHz oscillator
input frequency. With Lucas' recent commit b8cb519 ("tegra2: trivially
enable 13 mhz crystal frequency) applied, this breaks on hardware that
provides a different frequency.

The Tegra clock and reset controller provides a means to detect the
input frequency by counting the number of oscillator periods within a
given number of 32 kHz periods. This commit introduces a function,
clock_detect_osc_freq(), which performs this calibration and programs
the CRC_OSC_CTRL accordingly.

This code is loosely based on the Linux kernel Tegra2 clock code.

Signed-off-by: Thierry Reding <thierry.red...@avionic-design.de>
---
 arch/arm/cpu/armv7/tegra2/ap20.c           |    2 ++
 arch/arm/cpu/armv7/tegra2/clock.c          |   42 ++++++++++++++++++++++++++++
 arch/arm/include/asm/arch-tegra2/clk_rst.h |    9 ++++++
 arch/arm/include/asm/arch-tegra2/clock.h   |    3 ++
 4 files changed, 56 insertions(+)

diff --git a/arch/arm/cpu/armv7/tegra2/ap20.c b/arch/arm/cpu/armv7/tegra2/ap20.c
index 698bfd0..150c713 100644
--- a/arch/arm/cpu/armv7/tegra2/ap20.c
+++ b/arch/arm/cpu/armv7/tegra2/ap20.c
@@ -351,6 +351,8 @@ void tegra2_start(void)
                /* not reached */
        }
 
+       clock_detect_osc_freq();
+
        /* Init PMC scratch memory */
        init_pmc_scratch();
 
diff --git a/arch/arm/cpu/armv7/tegra2/clock.c 
b/arch/arm/cpu/armv7/tegra2/clock.c
index ccad351..77aefbc 100644
--- a/arch/arm/cpu/armv7/tegra2/clock.c
+++ b/arch/arm/cpu/armv7/tegra2/clock.c
@@ -396,6 +396,48 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = {
        NONE(CRAM2),
 };
 
+void clock_detect_osc_freq(void)
+{
+       struct clk_rst_ctlr *clkrst =
+                       (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+       enum clock_osc_freq frequency = CLOCK_OSC_FREQ_COUNT;
+       unsigned int periods;
+       u32 value;
+
+       /* start OSC frequency detection */
+       value = OSC_FREQ_DET_TRIGGER | REF_CLK_WIN_CFG(1);
+       writel(value, &clkrst->crc_osc_freq_det);
+
+       /* wait for detection to complete */
+       do {
+               value = readl(&clkrst->crc_osc_freq_det_stat);
+               if (!(value & OSC_FREQ_DET_BUSY))
+                       break;
+       } while (1);
+
+       periods = (value & OSC_FREQ_DET_CNT_MASK) >> OSC_FREQ_DET_CNT_SHIFT;
+
+       if (periods >= 732 - 3 && periods <= 732 + 3)
+               frequency = CLOCK_OSC_FREQ_12_0;
+       else if (periods >= 794 - 3 && periods <= 794 + 3)
+               frequency = CLOCK_OSC_FREQ_13_0;
+       else if (periods >= 1172 - 3 && periods <= 1172 + 3)
+               frequency = CLOCK_OSC_FREQ_19_2;
+       else if (periods >= 1587 - 3 && periods <= 1587 + 3)
+               frequency = CLOCK_OSC_FREQ_26_0;
+
+       /*
+        * Configure oscillator frequency. If the measured frequency isn't
+        * among those supported, keep the default and hope for the best.
+        */
+       if (frequency >= CLOCK_OSC_FREQ_COUNT) {
+               value = readl(&clkrst->crc_osc_ctrl);
+               value &= ~OSC_FREQ_MASK;
+               value |= frequency << OSC_FREQ_SHIFT;
+               writel(value, &clkrst->crc_osc_ctrl);
+       }
+}
+
 /*
  * Get the oscillator frequency, from the corresponding hardware configuration
  * field.
diff --git a/arch/arm/include/asm/arch-tegra2/clk_rst.h 
b/arch/arm/include/asm/arch-tegra2/clk_rst.h
index 8c3be91..66ca3ff 100644
--- a/arch/arm/include/asm/arch-tegra2/clk_rst.h
+++ b/arch/arm/include/asm/arch-tegra2/clk_rst.h
@@ -128,6 +128,15 @@ struct clk_rst_ctlr {
 #define OSC_XOBP_SHIFT         1
 #define OSC_XOBP_MASK          (1U << OSC_XOBP_SHIFT)
 
+/* CLK_RST_CONTROLLER_OSC_FREQ_DET_0 */
+#define OSC_FREQ_DET_TRIGGER   (1 << 31)
+#define REF_CLK_WIN_CFG(x)     ((x) & 0xf)
+
+/* CLK_RST_CONTROLLER_OSC_FREQ_DET_STATUS_0 */
+#define OSC_FREQ_DET_BUSY      (1 << 31)
+#define OSC_FREQ_DET_CNT_SHIFT 0
+#define OSC_FREQ_DET_CNT_MASK  (0xffff << OSC_FREQ_DET_CNT_SHIFT)
+
 /*
  * CLK_RST_CONTROLLER_CLK_SOURCE_x_OUT_0 - the mask here is normally 8 bits
  * but can be 16. We could use knowledge we have to restrict the mask in
diff --git a/arch/arm/include/asm/arch-tegra2/clock.h 
b/arch/arm/include/asm/arch-tegra2/clock.h
index 1d3ae38..a86db42 100644
--- a/arch/arm/include/asm/arch-tegra2/clock.h
+++ b/arch/arm/include/asm/arch-tegra2/clock.h
@@ -192,6 +192,9 @@ enum periph_id {
 /* PLL stabilization delay in usec */
 #define CLOCK_PLL_STABLE_DELAY_US 300
 
+/* detects the oscillator clock frequency */
+void clock_detect_osc_freq(void);
+
 /* return the current oscillator clock frequency */
 enum clock_osc_freq clock_get_osc_freq(void);
 
-- 
1.7.10.2

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to