[U-Boot] [PATCH] tegra: Implement oscillator frequency detection
Thierry Reding
thierry.reding at avionic-design.de
Thu May 24 09:03:13 CEST 2012
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.reding at 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
More information about the U-Boot
mailing list