summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2015-04-14 21:03:34 -0600
committerTom Warren <twarren@nvidia.com>2015-05-13 09:24:09 -0700
commit96e82a253a4c3a122de5023d9ca5fe04d9e19502 (patch)
treec6fa11ecd9601ba006d23c83b060a99151029af6 /arch/arm/mach-tegra
parent7bb6199bd6c95c89bb9b8e2e2890223e2bca6595 (diff)
downloadtalos-obmc-uboot-96e82a253a4c3a122de5023d9ca5fe04d9e19502.tar.gz
talos-obmc-uboot-96e82a253a4c3a122de5023d9ca5fe04d9e19502.zip
tegra124: clock: Add display clocks and functions
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>
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r--arch/arm/mach-tegra/clock.c2
-rw-r--r--arch/arm/mach-tegra/tegra124/clock.c141
2 files changed, 138 insertions, 5 deletions
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 4b58cc187e..cdd54388c5 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -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 */
diff --git a/arch/arm/mach-tegra/tegra124/clock.c b/arch/arm/mach-tegra/tegra124/clock.c
index fc8bd194dd..2d17550f73 100644
--- a/arch/arm/mach-tegra/tegra124/clock.c
+++ b/arch/arm/mach-tegra/tegra124/clock.c
@@ -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;
+}
OpenPOWER on IntegriCloud