[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