From: Simon Glass Date: Wed, 15 Apr 2015 03:03:34 +0000 (-0600) Subject: tegra124: clock: Add display clocks and functions X-Git-Url: http://git.dujemihanovic.xyz/img/sics.gif?a=commitdiff_plain;h=96e82a253a4c3a122de5023d9ca5fe04d9e19502;p=u-boot.git 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 Signed-off-by: Tom Warren --- diff --git a/arch/arm/include/asm/arch-tegra/clk_rst.h b/arch/arm/include/asm/arch-tegra/clk_rst.h index 7d28e16f1c..de50e08201 100644 --- a/arch/arm/include/asm/arch-tegra/clk_rst.h +++ b/arch/arm/include/asm/arch-tegra/clk_rst.h @@ -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_ */ diff --git a/arch/arm/include/asm/arch-tegra124/clock-tables.h b/arch/arm/include/asm/arch-tegra124/clock-tables.h index daf9a2b351..7005855999 100644 --- a/arch/arm/include/asm/arch-tegra124/clock-tables.h +++ b/arch/arm/include/asm/arch-tegra124/clock-tables.h @@ -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, diff --git a/arch/arm/include/asm/arch-tegra124/clock.h b/arch/arm/include/asm/arch-tegra124/clock.h index 8e65086252..e202cc5a7f 100644 --- a/arch/arm/include/asm/arch-tegra124/clock.h +++ b/arch/arm/include/asm/arch-tegra124/clock.h @@ -16,6 +16,27 @@ #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_ */ 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, ÷r_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; +}