Switch ARM clock source when changing the APLL frequency to avoid stability issues. This is ported from https://gerrit.chromium.org/gerrit/#/c/64189/5 Signed-off-by: David Hendricks <dhendrix@chromium.org> Change-Id: I923107555e6d3287b3694cbf9e4bb548d3e5f4a8 Reviewed-on: https://gerrit.chromium.org/gerrit/64838 Reviewed-by: David Hendricks <dhendrix@chromium.org> Tested-by: David Hendricks <dhendrix@chromium.org> Commit-Queue: David Hendricks <dhendrix@chromium.org> Reviewed-on: http://review.coreboot.org/4442 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
226 lines
6.7 KiB
C
226 lines
6.7 KiB
C
/*
|
|
* This file is part of the coreboot project.
|
|
*
|
|
* Copyright (C) 2012 Samsung Electronics
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/* Clock setup for SMDK5420 board based on EXYNOS5 */
|
|
|
|
#include <console/console.h>
|
|
#include <delay.h>
|
|
#include "clk.h"
|
|
#include "cpu.h"
|
|
#include "dp.h"
|
|
#include "dmc.h"
|
|
#include "setup.h"
|
|
|
|
void system_clock_init(void)
|
|
{
|
|
struct exynos5420_clock *clk =
|
|
(struct exynos5420_clock *)EXYNOS5420_CLOCK_BASE;
|
|
struct exynos5_mct_regs *mct_regs =
|
|
(struct exynos5_mct_regs *)EXYNOS5_MULTI_CORE_TIMER_BASE;
|
|
u32 val;
|
|
|
|
/* Turn on the MCT as early as possible. */
|
|
mct_regs->g_tcon |= (1 << 8);
|
|
|
|
/* PLL locktime */
|
|
writel(APLL_LOCK_VAL, &clk->apll_lock);
|
|
writel(MPLL_LOCK_VAL, &clk->mpll_lock);
|
|
writel(BPLL_LOCK_VAL, &clk->bpll_lock);
|
|
writel(CPLL_LOCK_VAL, &clk->cpll_lock);
|
|
writel(DPLL_LOCK_VAL, &clk->dpll_lock);
|
|
writel(EPLL_LOCK_VAL, &clk->epll_lock);
|
|
writel(VPLL_LOCK_VAL, &clk->vpll_lock);
|
|
writel(IPLL_LOCK_VAL, &clk->ipll_lock);
|
|
writel(SPLL_LOCK_VAL, &clk->spll_lock);
|
|
writel(KPLL_LOCK_VAL, &clk->kpll_lock);
|
|
writel(RPLL_LOCK_VAL, &clk->rpll_lock);
|
|
|
|
setbits_le32(&clk->clk_src_cpu, MUX_HPM_SEL_MASK);
|
|
|
|
writel(0, &clk->clk_src_top6);
|
|
|
|
writel(0, &clk->clk_src_cdrex);
|
|
writel(SRC_KFC_HPM_SEL, &clk->clk_src_kfc);
|
|
writel(HPM_RATIO, &clk->clk_div_cpu1);
|
|
writel(CLK_DIV_CPU0_VAL, &clk->clk_div_cpu0);
|
|
|
|
/* switch A15 clock source to OSC clock before changing APLL */
|
|
clrbits_le32(&clk->clk_src_cpu, APLL_FOUT);
|
|
|
|
/* Set APLL */
|
|
writel(APLL_CON1_VAL, &clk->apll_con1);
|
|
val = set_pll(0xc8, 0x3, 0x1);
|
|
writel(val, &clk->apll_con0);
|
|
while ((readl(&clk->apll_con0) & PLL_LOCKED) == 0)
|
|
;
|
|
|
|
/* now it is safe to switch to APLL */
|
|
setbits_le32(&clk->clk_src_cpu, APLL_FOUT);
|
|
|
|
writel(SRC_KFC_HPM_SEL, &clk->clk_src_kfc);
|
|
writel(CLK_DIV_KFC_VAL, &clk->clk_div_kfc0);
|
|
|
|
/* switch A7 clock source to OSC clock before changing KPLL */
|
|
clrbits_le32(&clk->clk_src_kfc, KPLL_FOUT);
|
|
|
|
/* Set KPLL*/
|
|
writel(KPLL_CON1_VAL, &clk->kpll_con1);
|
|
val = set_pll(0xc8, 0x2, 0x2);
|
|
writel(val, &clk->kpll_con0);
|
|
while ((readl(&clk->kpll_con0) & PLL_LOCKED) == 0)
|
|
;
|
|
|
|
/* now it is safe to switch to KPLL */
|
|
setbits_le32(&clk->clk_src_kfc, KPLL_FOUT);
|
|
|
|
/* Set MPLL */
|
|
writel(MPLL_CON1_VAL, &clk->mpll_con1);
|
|
val = set_pll(0xc8, 0x3, 0x1);
|
|
writel(val, &clk->mpll_con0);
|
|
while ((readl(&clk->mpll_con0) & PLL_LOCKED) == 0)
|
|
;
|
|
|
|
/* Set DPLL */
|
|
writel(DPLL_CON1_VAL, &clk->dpll_con1);
|
|
val = set_pll(0x190, 0x4, 0x2);
|
|
writel(val, &clk->dpll_con0);
|
|
while ((readl(&clk->dpll_con0) & PLL_LOCKED) == 0)
|
|
;
|
|
|
|
/* Set EPLL */
|
|
writel(EPLL_CON2_VAL, &clk->epll_con2);
|
|
writel(EPLL_CON1_VAL, &clk->epll_con1);
|
|
val = set_pll(0x64, 0x2, 0x1);
|
|
writel(val, &clk->epll_con0);
|
|
while ((readl(&clk->epll_con0) & PLL_LOCKED) == 0)
|
|
;
|
|
|
|
/* Set CPLL */
|
|
writel(CPLL_CON1_VAL, &clk->cpll_con1);
|
|
val = set_pll(0x6f, 0x2, 0x1);
|
|
writel(val, &clk->cpll_con0);
|
|
while ((readl(&clk->cpll_con0) & PLL_LOCKED) == 0)
|
|
;
|
|
|
|
/* Set IPLL */
|
|
writel(IPLL_CON1_VAL, &clk->ipll_con1);
|
|
val = set_pll(0xB9, 0x3, 0x2);
|
|
writel(val, &clk->ipll_con0);
|
|
while ((readl(&clk->ipll_con0) & PLL_LOCKED) == 0)
|
|
;
|
|
|
|
/* Set VPLL */
|
|
writel(VPLL_CON1_VAL, &clk->vpll_con1);
|
|
val = set_pll(0xd7, 0x3, 0x2);
|
|
writel(val, &clk->vpll_con0);
|
|
while ((readl(&clk->vpll_con0) & PLL_LOCKED) == 0)
|
|
;
|
|
|
|
/* Set BPLL */
|
|
writel(BPLL_CON1_VAL, &clk->bpll_con1);
|
|
val = set_pll(0xc8, 0x3, 0x1);
|
|
writel(val, &clk->bpll_con0);
|
|
while ((readl(&clk->bpll_con0) & PLL_LOCKED) == 0)
|
|
;
|
|
|
|
/* Set SPLL */
|
|
writel(SPLL_CON1_VAL, &clk->spll_con1);
|
|
val = set_pll(0xc8, 0x2, 0x3);
|
|
writel(val, &clk->spll_con0);
|
|
while ((readl(&clk->spll_con0) & PLL_LOCKED) == 0)
|
|
;
|
|
|
|
/* We use RPLL as the source for FIMD video stream clock */
|
|
writel(RPLL_CON1_VAL, &clk->rpll_con1);
|
|
writel(RPLL_CON2_VAL, &clk->rpll_con2);
|
|
/* computed by gabe from first principles; u-boot is probably
|
|
* wrong again
|
|
*/
|
|
val = set_pll(0xa0, 0x3, 0x2);
|
|
writel(val, &clk->rpll_con0);
|
|
/* note: this is a meaningless exercise. The hardware lock
|
|
* detection does not work. So this just spins for some
|
|
* time and is done. NO indication of success should attach
|
|
* to this or any other spin on a con0 value.
|
|
*/
|
|
while ((readl(&clk->rpll_con0) & PLL_LOCKED) == 0)
|
|
;
|
|
|
|
writel(CLK_DIV_CDREX0_VAL, &clk->clk_div_cdrex0);
|
|
writel(CLK_DIV_CDREX1_VAL, &clk->clk_div_cdrex1);
|
|
|
|
writel(CLK_SRC_TOP0_VAL, &clk->clk_src_top0);
|
|
writel(CLK_SRC_TOP1_VAL, &clk->clk_src_top1);
|
|
writel(CLK_SRC_TOP2_VAL, &clk->clk_src_top2);
|
|
writel(CLK_SRC_TOP7_VAL, &clk->clk_src_top7);
|
|
|
|
writel(CLK_DIV_TOP0_VAL, &clk->clk_div_top0);
|
|
writel(CLK_DIV_TOP1_VAL, &clk->clk_div_top1);
|
|
writel(CLK_DIV_TOP2_VAL, &clk->clk_div_top2);
|
|
|
|
writel(0, &clk->clk_src_top10);
|
|
writel(0, &clk->clk_src_top11);
|
|
writel(0, &clk->clk_src_top12);
|
|
|
|
writel(CLK_SRC_TOP3_VAL, &clk->clk_src_top3);
|
|
writel(CLK_SRC_TOP4_VAL, &clk->clk_src_top4);
|
|
writel(CLK_SRC_TOP5_VAL, &clk->clk_src_top5);
|
|
|
|
/* DISP1 BLK CLK SELECTION */
|
|
writel(CLK_SRC_DISP1_0_VAL, &clk->clk_src_disp10);
|
|
writel(CLK_DIV_DISP1_0_VAL, &clk->clk_div_disp10);
|
|
|
|
/* AUDIO BLK */
|
|
writel(AUDIO0_SEL_EPLL, &clk->clk_src_mau);
|
|
writel(DIV_MAU_VAL, &clk->clk_div_mau);
|
|
|
|
/* FSYS */
|
|
writel(CLK_SRC_FSYS0_VAL, &clk->clk_src_fsys);
|
|
writel(CLK_DIV_FSYS0_VAL, &clk->clk_div_fsys0);
|
|
writel(CLK_DIV_FSYS1_VAL, &clk->clk_div_fsys1);
|
|
writel(CLK_DIV_FSYS2_VAL, &clk->clk_div_fsys2);
|
|
|
|
writel(CLK_SRC_ISP_VAL, &clk->clk_src_isp);
|
|
writel(CLK_DIV_ISP0_VAL, &clk->clk_div_isp0);
|
|
writel(CLK_DIV_ISP1_VAL, &clk->clk_div_isp1);
|
|
|
|
writel(CLK_SRC_PERIC0_VAL, &clk->clk_src_peric0);
|
|
writel(CLK_SRC_PERIC1_VAL, &clk->clk_src_peric1);
|
|
|
|
writel(CLK_DIV_PERIC0_VAL, &clk->clk_div_peric0);
|
|
writel(CLK_DIV_PERIC1_VAL, &clk->clk_div_peric1);
|
|
writel(CLK_DIV_PERIC2_VAL, &clk->clk_div_peric2);
|
|
writel(CLK_DIV_PERIC3_VAL, &clk->clk_div_peric3);
|
|
writel(CLK_DIV_PERIC4_VAL, &clk->clk_div_peric4);
|
|
|
|
writel(CLK_DIV2_RATIO, &clk->clkdiv2_ratio);
|
|
writel(CLK_DIV4_RATIO, &clk->clkdiv4_ratio);
|
|
writel(CLK_DIV_G2D, &clk->clk_div_g2d);
|
|
|
|
writel(CLK_SRC_CPU_VAL, &clk->clk_src_cpu);
|
|
writel(CLK_SRC_TOP3_VAL, &clk->clk_src_top6);
|
|
writel(CLK_SRC_CDREX_VAL, &clk->clk_src_cdrex);
|
|
writel(CLK_SRC_KFC_VAL, &clk->clk_src_kfc);
|
|
}
|
|
|
|
void clock_gate(void)
|
|
{
|
|
/* Not implemented for now. */
|
|
}
|