rockchip: rk3399: add routines to set vop clocks

Let vop aclk sources from CPLL, and vop dclk from NPLL.

The dclk freq is decided by the edid mode pixel_clock which
may require high accuracy like 252750KHz. The pll_para_config()
can calculate the dividers for PLL to output desired clock.

BRANCH=none
BUG=chrome-os-partner:51537
TEST=check display with the other patches

Change-Id: I12cf27d3d1177a8b1c4cfbd7c0be10204e3d3142
Signed-off-by: Martin Roth <martinroth@google.com>
Original-Commit-Id: 0f019b055fffebe9ea3928aae1e25b0ad4feef81
Original-Change-Id: Icef58f87041905961772b69c6b8170d5a866a531
Original-Signed-off-by: Lin Huang <hl@rock-chips.com>
Original-Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/342335
Original-Tested-by: Vadim Bendebury <vbendeb@chromium.org>
Original-Reviewed-by: Vadim Bendebury <vbendeb@google.com>
Reviewed-on: https://review.coreboot.org/14846
Tested-by: build bot (Jenkins)
Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
This commit is contained in:
Shunqian Zheng 2016-05-04 15:54:37 +08:00 committed by Martin Roth
parent 2d96be6484
commit c7f32a5bb4
2 changed files with 123 additions and 0 deletions

View File

@ -159,6 +159,23 @@ enum {
CLK_SARADC_DIV_CON_MASK = 0xff,
CLK_SARADC_DIV_CON_SHIFT = 8,
/* CLKSEL_CON47 & CLKSEL_CON48 */
ACLK_VOP_PLL_SEL_MASK = 0x3,
ACLK_VOP_PLL_SEL_SHIFT = 6,
ACLK_VOP_PLL_SEL_CPLL = 0x1,
ACLK_VOP_DIV_CON_MASK = 0x1f,
ACLK_VOP_DIV_CON_SHIFT = 0,
/* CLKSEL_CON49 & CLKSEL_CON50 */
DCLK_VOP_DCLK_SEL_MASK = 1,
DCLK_VOP_DCLK_SEL_SHIFT = 11,
DCLK_VOP_DCLK_SEL_DIVOUT = 0,
DCLK_VOP_PLL_SEL_MASK = 3,
DCLK_VOP_PLL_SEL_SHIFT = 8,
DCLK_VOP_PLL_SEL_VPLL = 0,
DCLK_VOP_DIV_CON_MASK = 0xff,
DCLK_VOP_DIV_CON_SHIFT = 0,
/* CLKSEL_CON58 */
CLK_SPI_PLL_SEL_MASK = 1,
CLK_SPI_PLL_SEL_CPLL = 0,
@ -280,6 +297,70 @@ static void rkclk_set_pll(u32 *pll_con, const struct pll_div *div)
PLL_MODE_NORM << PLL_MODE_SHIFT));
}
static int pll_para_config(u32 freq_hz, struct pll_div *div)
{
u32 ref_khz = OSC_HZ / KHz, refdiv, fbdiv = 0;
u32 postdiv1, postdiv2 = 1;
u32 fref_khz;
u32 diff_khz, best_diff_khz;
const u32 max_refdiv = 63, max_fbdiv = 3200, min_fbdiv = 16;
const u32 max_postdiv1 = 7, max_postdiv2 = 7;
u32 vco_khz;
u32 freq_khz = freq_hz / KHz;
if (!freq_hz) {
printk(BIOS_ERR, "%s: the frequency can't be 0 Hz\n", __func__);
return -1;
}
postdiv1 = div_round_up(VCO_MIN_KHZ, freq_khz);
if (postdiv1 > max_postdiv1) {
postdiv2 = div_round_up(postdiv1, max_postdiv1);
postdiv1 = div_round_up(postdiv1, postdiv2);
}
vco_khz = freq_khz * postdiv1 * postdiv2;
if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ ||
postdiv2 > max_postdiv2) {
printk(BIOS_ERR, "%s: Cannot find out a supported VCO"
" for Frequency (%uHz).\n", __func__, freq_hz);
return -1;
}
div->postdiv1 = postdiv1;
div->postdiv2 = postdiv2;
best_diff_khz = vco_khz;
for (refdiv = 1; refdiv < max_refdiv && best_diff_khz; refdiv++) {
fref_khz = ref_khz / refdiv;
fbdiv = vco_khz / fref_khz;
if ((fbdiv >= max_fbdiv) || (fbdiv <= min_fbdiv))
continue;
diff_khz = vco_khz - fbdiv * fref_khz;
if (fbdiv + 1 < max_fbdiv && diff_khz > fref_khz / 2) {
fbdiv++;
diff_khz = fref_khz - diff_khz;
}
if (diff_khz >= best_diff_khz)
continue;
best_diff_khz = diff_khz;
div->refdiv = refdiv;
div->fbdiv = fbdiv;
}
if (best_diff_khz > 4 * (MHz/KHz)) {
printk(BIOS_ERR, "%s: Failed to match output frequency %u, "
"difference is %u Hz,exceed 4MHZ\n", __func__, freq_hz,
best_diff_khz * KHz);
return -1;
}
return 0;
}
void rkclk_init(void)
{
u32 aclk_div;
@ -593,3 +674,43 @@ void rkclk_configure_saradc(unsigned int hz)
CLK_SARADC_DIV_CON_SHIFT,
(src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT));
}
void rkclk_configure_vop_aclk(u32 vop_id, u32 aclk_hz)
{
u32 div;
void *reg_addr = vop_id ? &cru_ptr->clksel_con[48] :
&cru_ptr->clksel_con[47];
/* vop aclk source clk: cpll */
div = CPLL_HZ / aclk_hz;
assert((div - 1 < 32) && (div * aclk_hz == CPLL_HZ));
write32(reg_addr, RK_CLRSETBITS(
ACLK_VOP_PLL_SEL_MASK << ACLK_VOP_PLL_SEL_SHIFT |
ACLK_VOP_DIV_CON_MASK << ACLK_VOP_DIV_CON_SHIFT,
ACLK_VOP_PLL_SEL_CPLL << ACLK_VOP_PLL_SEL_SHIFT |
(div - 1) << ACLK_VOP_DIV_CON_SHIFT));
}
int rkclk_configure_vop_dclk(u32 vop_id, u32 dclk_hz)
{
struct pll_div vpll_config = {0};
void *reg_addr = vop_id ? &cru_ptr->clksel_con[50] :
&cru_ptr->clksel_con[49];
/* vop dclk source from vpll, and equals to vpll(means div == 1) */
if (pll_para_config(dclk_hz, &vpll_config))
return -1;
rkclk_set_pll(&cru_ptr->vpll_con[0], &vpll_config);
write32(reg_addr, RK_CLRSETBITS(
DCLK_VOP_DCLK_SEL_MASK << DCLK_VOP_DCLK_SEL_SHIFT |
DCLK_VOP_PLL_SEL_MASK << DCLK_VOP_PLL_SEL_SHIFT |
DCLK_VOP_DIV_CON_MASK << DCLK_VOP_DIV_CON_SHIFT,
DCLK_VOP_DCLK_SEL_DIVOUT << DCLK_VOP_DCLK_SEL_SHIFT |
DCLK_VOP_PLL_SEL_VPLL << DCLK_VOP_PLL_SEL_SHIFT |
(1 - 1) << DCLK_VOP_DIV_CON_SHIFT));
return 0;
}

View File

@ -103,10 +103,12 @@ enum apll_l_frequencies {
};
void rkclk_init(void);
int rkclk_configure_vop_dclk(u32 vop_id, u32 dclk_hz);
void rkclk_configure_cpu(enum apll_l_frequencies apll_l_freq);
void rkclk_configure_ddr(unsigned int hz);
void rkclk_configure_saradc(unsigned int hz);
void rkclk_configure_spi(unsigned int bus, unsigned int hz);
void rkclk_configure_vop_aclk(u32 vop_id, u32 aclk_hz);
void rkclk_ddr_reset(u32 ch, u32 ctl, u32 phy);
uint32_t rkclk_i2c_clock_for_bus(unsigned bus);