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:
parent
7bb6199bd6
commit
96e82a253a
@ -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_ */
|
||||
|
@ -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,
|
||||
|
@ -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_ */
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user