summaryrefslogtreecommitdiff
path: root/src/soc/rockchip/rk3288/clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/rockchip/rk3288/clock.c')
-rw-r--r--src/soc/rockchip/rk3288/clock.c156
1 files changed, 153 insertions, 3 deletions
diff --git a/src/soc/rockchip/rk3288/clock.c b/src/soc/rockchip/rk3288/clock.c
index d95257f610..c69c90baec 100644
--- a/src/soc/rockchip/rk3288/clock.c
+++ b/src/soc/rockchip/rk3288/clock.c
@@ -186,10 +186,22 @@ static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2);
#define GPLL_MODE_SLOW (0 << 12)
#define GPLL_MODE_NORM (1 << 12)
+#define NPLL_MODE_MSK (0x3 << 14)
+#define NPLL_MODE_SLOW (0 << 14)
+#define NPLL_MODE_NORM (1 << 14)
+
#define SOCSTS_DPLL_LOCK (1 << 5)
#define SOCSTS_APLL_LOCK (1 << 6)
#define SOCSTS_CPLL_LOCK (1 << 7)
#define SOCSTS_GPLL_LOCK (1 << 8)
+#define SOCSTS_NPLL_LOCK (1 << 9)
+
+#define VCO_MAX_KHZ (2200 * (MHz/KHz))
+#define VCO_MIN_KHZ (440 * (MHz/KHz))
+#define OUTPUT_MAX_KHZ (2200 * (MHz/KHz))
+#define OUTPUT_MIN_KHZ 27500
+#define FREF_MAX_KHZ (2200 * (MHz/KHz))
+#define FREF_MIN_KHZ 269
static int rkclk_set_pll(u32 *pll_con, const struct pll_div *div)
{
@@ -200,8 +212,8 @@ static int rkclk_set_pll(u32 *pll_con, const struct pll_div *div)
printk(BIOS_DEBUG, "Configuring PLL at %p with NF = %d, NR = %d and "
"NO = %d (VCO = %uKHz, output = %uKHz)\n",
pll_con, div->nf, div->nr, div->no, vco_khz, output_khz);
- assert(vco_khz >= 440*(MHz/KHz) && vco_khz <= 2200*(MHz/KHz) &&
- output_khz >= 27500 && output_khz <= 2200*(MHz/KHz) &&
+ assert(vco_khz >= VCO_MIN_KHZ && vco_khz <= VCO_MAX_KHZ &&
+ output_khz >= OUTPUT_MIN_KHZ && output_khz <= OUTPUT_MAX_KHZ &&
(div->no == 1 || !(div->no % 2)));
/* enter rest */
@@ -317,7 +329,7 @@ void rkclk_init(void)
}
-void rkclk_configure_cpu()
+void rkclk_configure_cpu(void)
{
/* pll enter slow-mode */
writel(RK_CLRSETBITS(APLL_MODE_MSK, APLL_MODE_SLOW),
@@ -491,3 +503,141 @@ void rkclk_configure_tsadc(unsigned int hz)
writel(RK_CLRSETBITS(0x3f << 0, (div - 1) << 0),
&cru_ptr->cru_clksel_con[2]);
}
+
+static int pll_para_config(u32 freq_hz, struct pll_div *div)
+{
+ u32 ref_khz = OSC_HZ / KHz, nr, nf = 0;
+ u32 fref_khz;
+ u32 diff_khz, best_diff_khz;
+ const u32 max_nr = 1 << 6, max_nf = 1 << 12, max_no = 1 << 4;
+ u32 vco_khz;
+ u32 no = 1;
+ u32 freq_khz = freq_hz / KHz;
+
+ if (!freq_hz) {
+ printk(BIOS_ERR, "%s: the frequency can not be 0 Hz\n", __func__);
+ return -1;
+ }
+ no = div_round_up(VCO_MIN_KHZ, freq_khz);
+
+ /* only even divisors (and 1) are supported */
+ if (no > 1)
+ no = div_round_up(no, 2) * 2;
+ vco_khz = freq_khz * no;
+ if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ || no > max_no) {
+ printk(BIOS_ERR, "%s: Cannot find out a supported VCO"
+ " for Frequency (%uHz).\n", __func__, freq_hz);
+ return -1;
+ }
+ div->no = no;
+
+ best_diff_khz = vco_khz;
+ for (nr = 1; nr < max_nr && best_diff_khz; nr++) {
+ fref_khz = ref_khz / nr;
+ if (fref_khz < FREF_MIN_KHZ)
+ break;
+ if (fref_khz > FREF_MAX_KHZ)
+ continue;
+
+ nf = vco_khz / fref_khz;
+ if (nf >= max_nf)
+ continue;
+ diff_khz = vco_khz - nf * fref_khz;
+ if (nf + 1 < max_nf && diff_khz > fref_khz / 2) {
+ nf++;
+ diff_khz = fref_khz - diff_khz;
+ }
+
+ if (diff_khz >= best_diff_khz)
+ continue;
+
+ best_diff_khz = diff_khz;
+ div->nr = nr;
+ div->nf = nf;
+ }
+
+ 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_configure_edp(void)
+{
+ /* rst edp */
+ writel(RK_SETBITS(1 << 15), &cru_ptr->cru_softrst_con[6]);
+ udelay(1);
+ writel(RK_CLRBITS(1 << 15), &cru_ptr->cru_softrst_con[6]);
+
+ /* clk_edp_24M source: 24M */
+ writel(RK_SETBITS(1 << 15), &cru_ptr->cru_clksel_con[28]);
+}
+
+void rkclk_configure_vop_aclk(u32 vop_id, u32 aclk_hz)
+{
+ u32 div;
+
+ /* vop aclk source clk: cpll */
+ div = CPLL_HZ / aclk_hz;
+ assert((div - 1 < 64) && (div * aclk_hz == CPLL_HZ));
+
+ switch (vop_id) {
+ case 0:
+ writel(RK_CLRSETBITS(3 << 6 | 0x1f << 0,
+ 0 << 6 | (div - 1) << 0),
+ &cru_ptr->cru_clksel_con[31]);
+ break;
+
+ case 1:
+ writel(RK_CLRSETBITS(3 << 14 | 0x1f << 8,
+ 0 << 14 | (div - 1) << 8),
+ &cru_ptr->cru_clksel_con[31]);
+ break;
+ }
+}
+
+
+int rkclk_configure_vop_dclk(u32 vop_id, u32 dclk_hz)
+{
+ struct pll_div npll_config = {0};
+
+ if (pll_para_config(dclk_hz, &npll_config))
+ return -1;
+
+ /* npll enter slow-mode */
+ writel(RK_CLRSETBITS(NPLL_MODE_MSK, NPLL_MODE_SLOW),
+ &cru_ptr->cru_mode_con);
+
+ rkclk_set_pll(&cru_ptr->cru_npll_con[0], &npll_config);
+
+ /* waiting for pll lock */
+ while (1) {
+ if (readl(&rk3288_grf->soc_status[1]) & SOCSTS_NPLL_LOCK)
+ break;
+ udelay(1);
+ }
+
+ /* npll enter normal-mode */
+ writel(RK_CLRSETBITS(NPLL_MODE_MSK, NPLL_MODE_NORM),
+ &cru_ptr->cru_mode_con);
+
+ /* vop dclk source clk: npll,dclk_div: 1 */
+ switch (vop_id) {
+ case 0:
+ writel(RK_CLRSETBITS(0xff << 8 | 3 << 0,
+ 0 << 8 | 2 << 0),
+ &cru_ptr->cru_clksel_con[27]);
+ break;
+
+ case 1:
+ writel(RK_CLRSETBITS(0xff << 8 | 3 << 6,
+ 0 << 8 | 2 << 6),
+ &cru_ptr->cru_clksel_con[29]);
+ break;
+ }
+ return 0;
+}