tegra20: add clock_set_pllout function
authorLucas Stach <dev@lynxeye.de>
Tue, 25 Sep 2012 20:21:13 +0000 (20:21 +0000)
committerTom Rini <trini@ti.com>
Mon, 15 Oct 2012 18:54:07 +0000 (11:54 -0700)
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 <dev@lynxeye.de>
Signed-off-by: Tom Warren <twarren@nvidia.com>
arch/arm/cpu/tegra20-common/clock.c
arch/arm/cpu/tegra20-common/warmboot_avp.c
arch/arm/include/asm/arch-tegra/clk_rst.h
arch/arm/include/asm/arch-tegra/clock.h
arch/arm/include/asm/arch-tegra20/clock-tables.h

index a670b07..12987a6 100644 (file)
@@ -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
index bc6281d..bc46606 100644 (file)
@@ -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 |
index 8c3be91..7b548c2 100644 (file)
@@ -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)
index 3eff163..eac1dc2 100644 (file)
@@ -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)
index f2b2769..53708e0 100644 (file)
@@ -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)