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