tegra124: clock: Add display clocks and functions
authorSimon Glass <sjg@chromium.org>
Wed, 15 Apr 2015 03:03:34 +0000 (21:03 -0600)
committerTom Warren <twarren@nvidia.com>
Wed, 13 May 2015 16:24:09 +0000 (09:24 -0700)
Add functions to provide access to the display clocks on Tegra124 including
setting the clock rate for an EDP display.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Tom Warren <twarren@nvidia.com>
arch/arm/include/asm/arch-tegra/clk_rst.h
arch/arm/include/asm/arch-tegra124/clock-tables.h
arch/arm/include/asm/arch-tegra124/clock.h
arch/arm/mach-tegra/clock.c
arch/arm/mach-tegra/tegra124/clock.c

index 7d28e16f1c5b8710d82977974cc51476685d522d..de50e082019cf0af53274cae3f18fb210d1d9fa3 100644 (file)
@@ -202,9 +202,13 @@ struct clk_rst_ctlr {
        uint crc_reserved52[1];         /* _reserved_52, 0x554 */
        uint crc_super_gr3d_clk_div;    /* _SUPER_GR3D_CLK_DIVIDER_0, 0x558 */
        uint crc_spare_reg0;            /* _SPARE_REG0_0, 0x55C */
-
-       /* Tegra124 - skip to 0x600 here for new CLK_SOURCE_ regs */
-       uint crc_reserved60[40];        /* _reserved_60, 0x560 - 0x5FC */
+       u32 _rsv32[4];                  /*                    0x560-0x56c */
+       u32 crc_plld2_ss_cfg;           /* _PLLD2_SS_CFG            0x570 */
+       u32 _rsv32_1[7];                /*                      0x574-58c */
+       struct clk_pll_simple plldp;    /* _PLLDP_BASE, 0x590 _PLLDP_MISC */
+       u32 crc_plldp_ss_cfg;           /* _PLLDP_SS_CFG, 0x598 */
+       u32 _rsrv32_2[25];
+       /* Tegra124 */
        uint crc_clk_src_x[TEGRA_CLK_SOURCES_X]; /* XUSB, etc, 0x600-0x678 */
 };
 
@@ -440,4 +444,9 @@ enum {
 #define PLLX_IDDQ_SHIFT                        3
 #define PLLX_IDDQ_MASK                 (1U << PLLX_IDDQ_SHIFT)
 
+/* CLK_RST_PLLDP_SS_CFG */
+#define PLLDP_SS_CFG_CLAMP             (1 << 22)
+#define PLLDP_SS_CFG_UNDOCUMENTED      (1 << 24)
+#define PLLDP_SS_CFG_DITHER            (1 << 28)
+
 #endif /* _TEGRA_CLK_RST_H_ */
index daf9a2b351a2b4ff74c7d9b8306c5c8080012590..7005855999579819f4df14415521b6be62e4395d 100644 (file)
@@ -25,6 +25,7 @@ enum clock_id {
        CLOCK_ID_XCPU = CLOCK_ID_FIRST_SIMPLE,
        CLOCK_ID_EPCI,
        CLOCK_ID_SFROM32KHZ,
+       CLOCK_ID_DP,    /* Special for Tegra124 */
 
        /* These are the base clocks (inputs to the Tegra SoC) */
        CLOCK_ID_32KHZ,
@@ -424,7 +425,7 @@ enum periphc_internal_id {
 
        /* 0x58 */
        PERIPHC_58h,
-       PERIPHC_59h,
+       PERIPHC_SOR,
        PERIPHC_5ah,
        PERIPHC_5bh,
        PERIPHC_SATAOOB,
index 8e650862529e4046227753c4db3548b5c2fc441c..e202cc5a7f469a2d6533d8851032c0544c620886 100644 (file)
 #define OSC_FREQ_SHIFT          28
 #define OSC_FREQ_MASK           (0xF << OSC_FREQ_SHIFT)
 
+/* CLK_RST_CONTROLLER_CLK_SOURCE_SOR0_0 */
+#define SOR0_CLK_SEL0                  (1 << 14)
+#define SOR0_CLK_SEL1                  (1 << 15)
+
 int tegra_plle_enable(void);
 
+void clock_sor_enable_edp_clock(void);
+
+/**
+ * clock_set_display_rate() - Set the display clock rate
+ *
+ * @frequency: the requested PLLD frequency
+ *
+ * Return the PLLD frequenc (which may not quite what was requested), or 0
+ * on failure
+ */
+u32 clock_set_display_rate(u32 frequency);
+
+/**
+ * clock_set_up_plldp() - Set up the EDP clock ready for use
+ */
+void clock_set_up_plldp(void);
+
 #endif /* _TEGRA124_CLOCK_H_ */
index 4b58cc187e58037f1b3e3814ab4a3d45a8336904..cdd54388c520d574dd73a69a15730091a65f0282 100644 (file)
@@ -593,6 +593,7 @@ void clock_init(void)
        pll_rate[CLOCK_ID_MEMORY] = clock_get_rate(CLOCK_ID_MEMORY);
        pll_rate[CLOCK_ID_PERIPH] = clock_get_rate(CLOCK_ID_PERIPH);
        pll_rate[CLOCK_ID_CGENERAL] = clock_get_rate(CLOCK_ID_CGENERAL);
+       pll_rate[CLOCK_ID_DISPLAY] = clock_get_rate(CLOCK_ID_DISPLAY);
        pll_rate[CLOCK_ID_OSC] = clock_get_rate(CLOCK_ID_OSC);
        pll_rate[CLOCK_ID_SFROM32KHZ] = 32768;
        pll_rate[CLOCK_ID_XCPU] = clock_get_rate(CLOCK_ID_XCPU);
@@ -600,6 +601,7 @@ void clock_init(void)
        debug("PLLM = %d\n", pll_rate[CLOCK_ID_MEMORY]);
        debug("PLLP = %d\n", pll_rate[CLOCK_ID_PERIPH]);
        debug("PLLC = %d\n", pll_rate[CLOCK_ID_CGENERAL]);
+       debug("PLLD = %d\n", pll_rate[CLOCK_ID_DISPLAY]);
        debug("PLLX = %d\n", pll_rate[CLOCK_ID_XCPU]);
 
        /* Do any special system timer/TSC setup */
index fc8bd194ddc9d7bb29f8c1503939c58f4bf47066..2d17550f733688c33dd752988670b4794f3d3b90 100644 (file)
@@ -42,6 +42,7 @@ enum clock_type_id {
        CLOCK_TYPE_ASPTE,
        CLOCK_TYPE_PMDACD2T,
        CLOCK_TYPE_PCST,
+       CLOCK_TYPE_DP,
 
        CLOCK_TYPE_PC2CC3M,
        CLOCK_TYPE_PC2CC3S_T,
@@ -101,6 +102,10 @@ static enum clock_id clock_source[CLOCK_TYPE_COUNT][CLOCK_MAX_MUX+1] = {
        { CLK(PERIPH),  CLK(CGENERAL),  CLK(SFROM32KHZ),        CLK(OSC),
                CLK(NONE),      CLK(NONE),      CLK(NONE),      CLK(NONE),
                MASK_BITS_31_28},
+       /* CLOCK_TYPE_DP */
+       { CLK(NONE),    CLK(NONE),      CLK(NONE),      CLK(NONE),
+               CLK(NONE),      CLK(NONE),      CLK(NONE),      CLK(NONE),
+               MASK_BITS_31_28},
 
        /* Additional clock types on Tegra114+ */
        /* CLOCK_TYPE_PC2CC3M */
@@ -259,7 +264,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = {
 
        /* 0x58 */
        TYPE(PERIPHC_58h,       CLOCK_TYPE_NONE),
-       TYPE(PERIPHC_59h,       CLOCK_TYPE_NONE),
+       TYPE(PERIPHC_SOR,       CLOCK_TYPE_NONE),
        TYPE(PERIPHC_5ah,       CLOCK_TYPE_NONE),
        TYPE(PERIPHC_5bh,       CLOCK_TYPE_NONE),
        TYPE(PERIPHC_SATAOOB,   CLOCK_TYPE_PCMT),
@@ -546,7 +551,7 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = {
        NONE(X_RESERVED19),
        NONE(ADX1),
        NONE(DPAUX),
-       NONE(SOR0),
+       PERIPHC_SOR,
        NONE(X_RESERVED23),
 
        /* 184 */
@@ -594,7 +599,10 @@ u32 *get_periph_source_reg(enum periph_id periph_id)
        assert(periph_id >= PERIPH_ID_FIRST && periph_id < PERIPH_ID_COUNT);
        internal_id = periph_id_to_internal_id[periph_id];
        assert(internal_id != -1);
-       if (internal_id >= PERIPHC_VW_FIRST) {
+       if (internal_id >= PERIPHC_X_FIRST) {
+               internal_id -= PERIPHC_X_FIRST;
+               return &clkrst->crc_clk_src_x[internal_id];
+       } else if (internal_id >= PERIPHC_VW_FIRST) {
                internal_id -= PERIPHC_VW_FIRST;
                return &clkrst->crc_clk_src_vw[internal_id];
        } else {
@@ -657,8 +665,10 @@ void clock_set_enable(enum periph_id periph_id, int enable)
        assert(clock_periph_id_isvalid(periph_id));
        if ((int)periph_id < (int)PERIPH_ID_VW_FIRST)
                clk = &clkrst->crc_clk_out_enb[PERIPH_REG(periph_id)];
-       else
+       else if ((int)periph_id < PERIPH_ID_X_FIRST)
                clk = &clkrst->crc_clk_out_enb_vw[PERIPH_REG(periph_id)];
+       else
+               clk = &clkrst->crc_clk_out_enb_x;
        reg = readl(clk);
        if (enable)
                reg |= PERIPH_MASK(periph_id);
@@ -678,8 +688,10 @@ void reset_set_enable(enum periph_id periph_id, int enable)
        assert(clock_periph_id_isvalid(periph_id));
        if (periph_id < PERIPH_ID_VW_FIRST)
                reset = &clkrst->crc_rst_dev[PERIPH_REG(periph_id)];
-       else
+       else if ((int)periph_id < PERIPH_ID_X_FIRST)
                reset = &clkrst->crc_rst_dev_vw[PERIPH_REG(periph_id)];
+       else
+               reset = &clkrst->crc_rst_devices_x;
        reg = readl(reset);
        if (enable)
                reg |= PERIPH_MASK(periph_id);
@@ -933,3 +945,122 @@ int tegra_plle_enable(void)
 
        return 0;
 }
+
+void clock_sor_enable_edp_clock(void)
+{
+       u32 *reg;
+
+       /* uses PLLP, has a non-standard bit layout. */
+       reg = get_periph_source_reg(PERIPH_ID_SOR0);
+       setbits_le32(reg, SOR0_CLK_SEL0);
+}
+
+u32 clock_set_display_rate(u32 frequency)
+{
+       /**
+        * plld (fo) = vco >> p, where 500MHz < vco < 1000MHz
+        *           = (cf * n) >> p, where 1MHz < cf < 6MHz
+        *           = ((ref / m) * n) >> p
+        *
+        * Iterate the possible values of p (3 bits, 2^7) to find out a minimum
+        * safe vco, then find best (m, n). since m has only 5 bits, we can
+        * iterate all possible values.  Note Tegra 124 supports 11 bits for n,
+        * but our pll_fields has only 10 bits for n.
+        *
+        * Note values undershoot or overshoot target output frequency may not
+        * work if the values are not in "safe" range by panel specification.
+        */
+       u32 ref = clock_get_rate(CLOCK_ID_OSC);
+       u32 divm, divn, divp, cpcon;
+       u32 cf, vco, rounded_rate = frequency;
+       u32 diff, best_diff, best_m = 0, best_n = 0, best_p;
+       const u32 max_m = 1 << 5, max_n = 1 << 10, max_p = 1 << 3,
+                 mhz = 1000 * 1000, min_vco = 500 * mhz, max_vco = 1000 * mhz,
+                 min_cf = 1 * mhz, max_cf = 6 * mhz;
+       int mux_bits, divider_bits, source;
+
+       for (divp = 0, vco = frequency; vco < min_vco && divp < max_p; divp++)
+               vco <<= 1;
+
+       if (vco < min_vco || vco > max_vco) {
+               printf("%s: Cannot find out a supported VCO for Frequency (%u)\n",
+                      __func__, frequency);
+               return 0;
+       }
+
+       best_p = divp;
+       best_diff = vco;
+
+       for (divm = 1; divm < max_m && best_diff; divm++) {
+               cf = ref / divm;
+               if (cf < min_cf)
+                       break;
+               if (cf > max_cf)
+                       continue;
+
+               divn = vco / cf;
+               if (divn >= max_n)
+                       continue;
+
+               diff = vco - divn * cf;
+               if (divn + 1 < max_n && diff > cf / 2) {
+                       divn++;
+                       diff = cf - diff;
+               }
+
+               if (diff >= best_diff)
+                       continue;
+
+               best_diff = diff;
+               best_m = divm;
+               best_n = divn;
+       }
+
+       if (best_n < 50)
+               cpcon = 2;
+       else if (best_n < 300)
+               cpcon = 3;
+       else if (best_n < 600)
+               cpcon = 8;
+       else
+               cpcon = 12;
+
+       if (best_diff) {
+               printf("%s: Failed to match output frequency %u, best difference is %u\n",
+                      __func__, frequency, best_diff);
+               rounded_rate = (ref / best_m * best_n) >> best_p;
+       }
+
+       debug("%s: PLLD=%u ref=%u, m/n/p/cpcon=%u/%u/%u/%u\n",
+             __func__, rounded_rate, ref, best_m, best_n, best_p, cpcon);
+
+       source = get_periph_clock_source(PERIPH_ID_DISP1, CLOCK_ID_DISPLAY,
+                                        &mux_bits, &divider_bits);
+       clock_ll_set_source_bits(PERIPH_ID_DISP1, mux_bits, source);
+       clock_set_rate(CLOCK_ID_DISPLAY, best_n, best_m, best_p, cpcon);
+
+       return rounded_rate;
+}
+
+void clock_set_up_plldp(void)
+{
+       struct clk_rst_ctlr *clkrst =
+                       (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+       u32 value;
+
+       value = PLLDP_SS_CFG_UNDOCUMENTED | PLLDP_SS_CFG_DITHER;
+       writel(value | PLLDP_SS_CFG_CLAMP, &clkrst->crc_plldp_ss_cfg);
+       clock_start_pll(CLOCK_ID_DP, 1, 90, 3, 0, 0);
+       writel(value, &clkrst->crc_plldp_ss_cfg);
+}
+
+struct clk_pll_simple *clock_get_simple_pll(enum clock_id clkid)
+{
+       struct clk_rst_ctlr *clkrst =
+                       (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+
+       if (clkid == CLOCK_ID_DP)
+               return &clkrst->plldp;
+
+       return NULL;
+}