mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-10-05 19:10:53 +09:00
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_reserved52[1]; /* _reserved_52, 0x554 */
|
||||||
uint crc_super_gr3d_clk_div; /* _SUPER_GR3D_CLK_DIVIDER_0, 0x558 */
|
uint crc_super_gr3d_clk_div; /* _SUPER_GR3D_CLK_DIVIDER_0, 0x558 */
|
||||||
uint crc_spare_reg0; /* _SPARE_REG0_0, 0x55C */
|
uint crc_spare_reg0; /* _SPARE_REG0_0, 0x55C */
|
||||||
|
u32 _rsv32[4]; /* 0x560-0x56c */
|
||||||
/* Tegra124 - skip to 0x600 here for new CLK_SOURCE_ regs */
|
u32 crc_plld2_ss_cfg; /* _PLLD2_SS_CFG 0x570 */
|
||||||
uint crc_reserved60[40]; /* _reserved_60, 0x560 - 0x5FC */
|
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 */
|
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_SHIFT 3
|
||||||
#define PLLX_IDDQ_MASK (1U << PLLX_IDDQ_SHIFT)
|
#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_ */
|
#endif /* _TEGRA_CLK_RST_H_ */
|
||||||
|
@ -25,6 +25,7 @@ enum clock_id {
|
|||||||
CLOCK_ID_XCPU = CLOCK_ID_FIRST_SIMPLE,
|
CLOCK_ID_XCPU = CLOCK_ID_FIRST_SIMPLE,
|
||||||
CLOCK_ID_EPCI,
|
CLOCK_ID_EPCI,
|
||||||
CLOCK_ID_SFROM32KHZ,
|
CLOCK_ID_SFROM32KHZ,
|
||||||
|
CLOCK_ID_DP, /* Special for Tegra124 */
|
||||||
|
|
||||||
/* These are the base clocks (inputs to the Tegra SoC) */
|
/* These are the base clocks (inputs to the Tegra SoC) */
|
||||||
CLOCK_ID_32KHZ,
|
CLOCK_ID_32KHZ,
|
||||||
@ -424,7 +425,7 @@ enum periphc_internal_id {
|
|||||||
|
|
||||||
/* 0x58 */
|
/* 0x58 */
|
||||||
PERIPHC_58h,
|
PERIPHC_58h,
|
||||||
PERIPHC_59h,
|
PERIPHC_SOR,
|
||||||
PERIPHC_5ah,
|
PERIPHC_5ah,
|
||||||
PERIPHC_5bh,
|
PERIPHC_5bh,
|
||||||
PERIPHC_SATAOOB,
|
PERIPHC_SATAOOB,
|
||||||
|
@ -16,6 +16,27 @@
|
|||||||
#define OSC_FREQ_SHIFT 28
|
#define OSC_FREQ_SHIFT 28
|
||||||
#define OSC_FREQ_MASK (0xF << OSC_FREQ_SHIFT)
|
#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);
|
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_ */
|
#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_MEMORY] = clock_get_rate(CLOCK_ID_MEMORY);
|
||||||
pll_rate[CLOCK_ID_PERIPH] = clock_get_rate(CLOCK_ID_PERIPH);
|
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_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_OSC] = clock_get_rate(CLOCK_ID_OSC);
|
||||||
pll_rate[CLOCK_ID_SFROM32KHZ] = 32768;
|
pll_rate[CLOCK_ID_SFROM32KHZ] = 32768;
|
||||||
pll_rate[CLOCK_ID_XCPU] = clock_get_rate(CLOCK_ID_XCPU);
|
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("PLLM = %d\n", pll_rate[CLOCK_ID_MEMORY]);
|
||||||
debug("PLLP = %d\n", pll_rate[CLOCK_ID_PERIPH]);
|
debug("PLLP = %d\n", pll_rate[CLOCK_ID_PERIPH]);
|
||||||
debug("PLLC = %d\n", pll_rate[CLOCK_ID_CGENERAL]);
|
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]);
|
debug("PLLX = %d\n", pll_rate[CLOCK_ID_XCPU]);
|
||||||
|
|
||||||
/* Do any special system timer/TSC setup */
|
/* Do any special system timer/TSC setup */
|
||||||
|
@ -42,6 +42,7 @@ enum clock_type_id {
|
|||||||
CLOCK_TYPE_ASPTE,
|
CLOCK_TYPE_ASPTE,
|
||||||
CLOCK_TYPE_PMDACD2T,
|
CLOCK_TYPE_PMDACD2T,
|
||||||
CLOCK_TYPE_PCST,
|
CLOCK_TYPE_PCST,
|
||||||
|
CLOCK_TYPE_DP,
|
||||||
|
|
||||||
CLOCK_TYPE_PC2CC3M,
|
CLOCK_TYPE_PC2CC3M,
|
||||||
CLOCK_TYPE_PC2CC3S_T,
|
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(PERIPH), CLK(CGENERAL), CLK(SFROM32KHZ), CLK(OSC),
|
||||||
CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE),
|
CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE),
|
||||||
MASK_BITS_31_28},
|
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+ */
|
/* Additional clock types on Tegra114+ */
|
||||||
/* CLOCK_TYPE_PC2CC3M */
|
/* CLOCK_TYPE_PC2CC3M */
|
||||||
@ -259,7 +264,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = {
|
|||||||
|
|
||||||
/* 0x58 */
|
/* 0x58 */
|
||||||
TYPE(PERIPHC_58h, CLOCK_TYPE_NONE),
|
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_5ah, CLOCK_TYPE_NONE),
|
||||||
TYPE(PERIPHC_5bh, CLOCK_TYPE_NONE),
|
TYPE(PERIPHC_5bh, CLOCK_TYPE_NONE),
|
||||||
TYPE(PERIPHC_SATAOOB, CLOCK_TYPE_PCMT),
|
TYPE(PERIPHC_SATAOOB, CLOCK_TYPE_PCMT),
|
||||||
@ -546,7 +551,7 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = {
|
|||||||
NONE(X_RESERVED19),
|
NONE(X_RESERVED19),
|
||||||
NONE(ADX1),
|
NONE(ADX1),
|
||||||
NONE(DPAUX),
|
NONE(DPAUX),
|
||||||
NONE(SOR0),
|
PERIPHC_SOR,
|
||||||
NONE(X_RESERVED23),
|
NONE(X_RESERVED23),
|
||||||
|
|
||||||
/* 184 */
|
/* 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);
|
assert(periph_id >= PERIPH_ID_FIRST && periph_id < PERIPH_ID_COUNT);
|
||||||
internal_id = periph_id_to_internal_id[periph_id];
|
internal_id = periph_id_to_internal_id[periph_id];
|
||||||
assert(internal_id != -1);
|
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;
|
internal_id -= PERIPHC_VW_FIRST;
|
||||||
return &clkrst->crc_clk_src_vw[internal_id];
|
return &clkrst->crc_clk_src_vw[internal_id];
|
||||||
} else {
|
} else {
|
||||||
@ -657,8 +665,10 @@ void clock_set_enable(enum periph_id periph_id, int enable)
|
|||||||
assert(clock_periph_id_isvalid(periph_id));
|
assert(clock_periph_id_isvalid(periph_id));
|
||||||
if ((int)periph_id < (int)PERIPH_ID_VW_FIRST)
|
if ((int)periph_id < (int)PERIPH_ID_VW_FIRST)
|
||||||
clk = &clkrst->crc_clk_out_enb[PERIPH_REG(periph_id)];
|
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)];
|
clk = &clkrst->crc_clk_out_enb_vw[PERIPH_REG(periph_id)];
|
||||||
|
else
|
||||||
|
clk = &clkrst->crc_clk_out_enb_x;
|
||||||
reg = readl(clk);
|
reg = readl(clk);
|
||||||
if (enable)
|
if (enable)
|
||||||
reg |= PERIPH_MASK(periph_id);
|
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));
|
assert(clock_periph_id_isvalid(periph_id));
|
||||||
if (periph_id < PERIPH_ID_VW_FIRST)
|
if (periph_id < PERIPH_ID_VW_FIRST)
|
||||||
reset = &clkrst->crc_rst_dev[PERIPH_REG(periph_id)];
|
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)];
|
reset = &clkrst->crc_rst_dev_vw[PERIPH_REG(periph_id)];
|
||||||
|
else
|
||||||
|
reset = &clkrst->crc_rst_devices_x;
|
||||||
reg = readl(reset);
|
reg = readl(reset);
|
||||||
if (enable)
|
if (enable)
|
||||||
reg |= PERIPH_MASK(periph_id);
|
reg |= PERIPH_MASK(periph_id);
|
||||||
@ -933,3 +945,122 @@ int tegra_plle_enable(void)
|
|||||||
|
|
||||||
return 0;
|
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