Skip to content
Snippets Groups Projects
Commit 65530a84 authored by Lucas Stach's avatar Lucas Stach Committed by Tom Rini
Browse files

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: default avatarLucas Stach <dev@lynxeye.de>
Signed-off-by: default avatarTom Warren <twarren@nvidia.com>
parent 3f44e44f
No related branches found
No related tags found
No related merge requests found
...@@ -396,6 +396,16 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = { ...@@ -396,6 +396,16 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = {
NONE(CRAM2), 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 * Get the oscillator frequency, from the corresponding hardware configuration
* field. * field.
...@@ -604,6 +614,34 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id, ...@@ -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); (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 * 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 * required child clock rate. This function assumes that a second-stage
......
...@@ -214,7 +214,7 @@ void wb_start(void) ...@@ -214,7 +214,7 @@ void wb_start(void)
reg = PLLM_OUT1_RSTN_RESET_DISABLE | PLLM_OUT1_CLKEN_ENABLE | reg = PLLM_OUT1_RSTN_RESET_DISABLE | PLLM_OUT1_CLKEN_ENABLE |
PLLM_OUT1_RATIO_VAL_8; 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 | 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 | SCLK_SWAKE_RUN_SRC_PLLM_OUT1 | SCLK_SWAKE_IDLE_SRC_PLLM_OUT1 |
......
...@@ -27,8 +27,7 @@ ...@@ -27,8 +27,7 @@
/* PLL registers - there are several PLLs in the clock controller */ /* PLL registers - there are several PLLs in the clock controller */
struct clk_pll { struct clk_pll {
uint pll_base; /* the control register */ uint pll_base; /* the control register */
uint pll_out; /* output control */ uint pll_out[2]; /* output control */
uint reserved;
uint pll_misc; /* other misc things */ uint pll_misc; /* other misc things */
}; };
...@@ -112,6 +111,14 @@ struct clk_rst_ctlr { ...@@ -112,6 +111,14 @@ struct clk_rst_ctlr {
#define PLL_DIVM_SHIFT 0 #define PLL_DIVM_SHIFT 0
#define PLL_DIVM_MASK (0x1f << PLL_DIVM_SHIFT) #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 */ /* CLK_RST_CONTROLLER_PLLx_MISC_0 */
#define PLL_CPCON_SHIFT 8 #define PLL_CPCON_SHIFT 8
#define PLL_CPCON_MASK (15U << PLL_CPCON_SHIFT) #define PLL_CPCON_MASK (15U << PLL_CPCON_SHIFT)
......
...@@ -57,6 +57,18 @@ enum clock_osc_freq clock_get_osc_freq(void); ...@@ -57,6 +57,18 @@ enum clock_osc_freq clock_get_osc_freq(void);
unsigned long clock_start_pll(enum clock_id id, u32 divm, u32 divn, unsigned long clock_start_pll(enum clock_id id, u32 divm, u32 divn,
u32 divp, u32 cpcon, u32 lfcon); 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. * Read low-level parameters of a PLL.
* *
......
...@@ -176,6 +176,13 @@ enum periph_id { ...@@ -176,6 +176,13 @@ enum periph_id {
PERIPH_ID_NONE = -1, 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 */ /* Converts a clock number to a clock register: 0=L, 1=H, 2=U */
#define PERIPH_REG(id) ((id) >> 5) #define PERIPH_REG(id) ((id) >> 5)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment