From 65530a842eeaf7ad07e0613ac6f883f2f1f1e33f Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Tue, 25 Sep 2012 20:21:13 +0000 Subject: [PATCH] tegra20: add clock_set_pllout function Common practice on Tegra 2 boards is to use the pllp_out4 FO to generate the ULPI reference clock. For this to work we have to override the default hardware generated output divider. This function adds a clean way to do so. Signed-off-by: Lucas Stach Signed-off-by: Tom Warren --- arch/arm/cpu/tegra20-common/clock.c | 38 ++++++++++++++++++++++++ arch/arm/cpu/tegra20-common/warmboot_avp.c | 2 +- arch/arm/include/asm/arch-tegra/clk_rst.h | 11 +++++-- arch/arm/include/asm/arch-tegra/clock.h | 12 ++++++++ arch/arm/include/asm/arch-tegra20/clock-tables.h | 7 +++++ 5 files changed, 67 insertions(+), 3 deletions(-) diff --git a/arch/arm/cpu/tegra20-common/clock.c b/arch/arm/cpu/tegra20-common/clock.c index a670b07..12987a6 100644 --- a/arch/arm/cpu/tegra20-common/clock.c +++ b/arch/arm/cpu/tegra20-common/clock.c @@ -396,6 +396,16 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = { NONE(CRAM2), }; +/* number of clock outputs of a PLL */ +static const u8 pll_num_clkouts[] = { + 1, /* PLLC */ + 1, /* PLLM */ + 4, /* PLLP */ + 1, /* PLLA */ + 0, /* PLLU */ + 0, /* PLLD */ +}; + /* * Get the oscillator frequency, from the corresponding hardware configuration * field. @@ -604,6 +614,34 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id, (readl(reg) & OUT_CLK_DIVISOR_MASK) >> OUT_CLK_DIVISOR_SHIFT); } +int clock_set_pllout(enum clock_id clkid, enum pll_out_id pllout, unsigned rate) +{ + struct clk_pll *pll = get_pll(clkid); + int data = 0, div = 0, offset = 0; + + if (!clock_id_is_pll(clkid)) + return -1; + + if (pllout + 1 > pll_num_clkouts[clkid]) + return -1; + + div = clk_get_divider(8, pll_rate[clkid], rate); + + if (div < 0) + return -1; + + /* out2 and out4 are in the high part of the register */ + if (pllout == PLL_OUT2 || pllout == PLL_OUT4) + offset = 16; + + data = (div << PLL_OUT_RATIO_SHIFT) | + PLL_OUT_OVRRIDE | PLL_OUT_CLKEN | PLL_OUT_RSTN; + clrsetbits_le32(&pll->pll_out[pllout >> 1], + PLL_OUT_RATIO_MASK << offset, data << offset); + + return 0; +} + /** * Find the best available 7.1 format divisor given a parent clock rate and * required child clock rate. This function assumes that a second-stage diff --git a/arch/arm/cpu/tegra20-common/warmboot_avp.c b/arch/arm/cpu/tegra20-common/warmboot_avp.c index bc6281d..bc46606 100644 --- a/arch/arm/cpu/tegra20-common/warmboot_avp.c +++ b/arch/arm/cpu/tegra20-common/warmboot_avp.c @@ -214,7 +214,7 @@ void wb_start(void) reg = PLLM_OUT1_RSTN_RESET_DISABLE | PLLM_OUT1_CLKEN_ENABLE | PLLM_OUT1_RATIO_VAL_8; - writel(reg, &clkrst->crc_pll[CLOCK_ID_MEMORY].pll_out); + writel(reg, &clkrst->crc_pll[CLOCK_ID_MEMORY].pll_out[0]); reg = SCLK_SWAKE_FIQ_SRC_PLLM_OUT1 | SCLK_SWAKE_IRQ_SRC_PLLM_OUT1 | SCLK_SWAKE_RUN_SRC_PLLM_OUT1 | SCLK_SWAKE_IDLE_SRC_PLLM_OUT1 | diff --git a/arch/arm/include/asm/arch-tegra/clk_rst.h b/arch/arm/include/asm/arch-tegra/clk_rst.h index 8c3be91..7b548c2 100644 --- a/arch/arm/include/asm/arch-tegra/clk_rst.h +++ b/arch/arm/include/asm/arch-tegra/clk_rst.h @@ -27,8 +27,7 @@ /* PLL registers - there are several PLLs in the clock controller */ struct clk_pll { uint pll_base; /* the control register */ - uint pll_out; /* output control */ - uint reserved; + uint pll_out[2]; /* output control */ uint pll_misc; /* other misc things */ }; @@ -112,6 +111,14 @@ struct clk_rst_ctlr { #define PLL_DIVM_SHIFT 0 #define PLL_DIVM_MASK (0x1f << PLL_DIVM_SHIFT) +/* CLK_RST_CONTROLLER_PLLx_OUTx_0 */ +#define PLL_OUT_RSTN (1 << 0) +#define PLL_OUT_CLKEN (1 << 1) +#define PLL_OUT_OVRRIDE (1 << 2) + +#define PLL_OUT_RATIO_SHIFT 8 +#define PLL_OUT_RATIO_MASK (0xffU << PLL_OUT_RATIO_SHIFT) + /* CLK_RST_CONTROLLER_PLLx_MISC_0 */ #define PLL_CPCON_SHIFT 8 #define PLL_CPCON_MASK (15U << PLL_CPCON_SHIFT) diff --git a/arch/arm/include/asm/arch-tegra/clock.h b/arch/arm/include/asm/arch-tegra/clock.h index 3eff163..eac1dc2 100644 --- a/arch/arm/include/asm/arch-tegra/clock.h +++ b/arch/arm/include/asm/arch-tegra/clock.h @@ -58,6 +58,18 @@ unsigned long clock_start_pll(enum clock_id id, u32 divm, u32 divn, u32 divp, u32 cpcon, u32 lfcon); /** + * Set PLL output frequency + * + * @param clkid clock id + * @param pllout pll output id + * @param rate desired output rate + * + * @return 0 if ok, -1 on error (invalid clock id or no suitable divider) + */ +int clock_set_pllout(enum clock_id clkid, enum pll_out_id pllout, + unsigned rate); + +/** * Read low-level parameters of a PLL. * * @param id clock id to read (note: USB is not supported) diff --git a/arch/arm/include/asm/arch-tegra20/clock-tables.h b/arch/arm/include/asm/arch-tegra20/clock-tables.h index f2b2769..53708e0 100644 --- a/arch/arm/include/asm/arch-tegra20/clock-tables.h +++ b/arch/arm/include/asm/arch-tegra20/clock-tables.h @@ -176,6 +176,13 @@ enum periph_id { PERIPH_ID_NONE = -1, }; +enum pll_out_id { + PLL_OUT1, + PLL_OUT2, + PLL_OUT3, + PLL_OUT4 +}; + /* Converts a clock number to a clock register: 0=L, 1=H, 2=U */ #define PERIPH_REG(id) ((id) >> 5) -- 2.7.4