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>
This commit is contained in:
Simon Glass 2015-04-14 21:03:34 -06:00 committed by Tom Warren
parent 7bb6199bd6
commit 96e82a253a
5 changed files with 173 additions and 9 deletions

View 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_ */

View 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,

View File

@ -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_ */

View 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 */

View 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;
}