blob: 494fcadbacaf246caaef56462605dd2d40e4f6c5 [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* Copyright 2015 MediaTek Inc.
*
* 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.
*/
#include <device/mmio.h>
#include <assert.h>
#include <delay.h>
#include <soc/addressmap.h>
#include <soc/infracfg.h>
#include <soc/pll.h>
#include <types.h>
enum mux_id {
TOP_AXI_SEL,
TOP_MEM_SEL,
TOP_DDRPHYCFG_SEL,
TOP_MM_SEL,
TOP_PWM_SEL,
TOP_VDEC_SEL,
TOP_VENC_SEL,
TOP_MFG_SEL,
TOP_CAMTG_SEL,
TOP_UART_SEL,
TOP_SPI_SEL,
TOP_USB20_SEL,
TOP_USB30_SEL,
TOP_MSDC50_0_H_SEL,
TOP_MSDC50_0_SEL,
TOP_MSDC30_1_SEL,
TOP_MSDC30_2_SEL,
TOP_MSDC30_3_SEL,
TOP_AUDIO_SEL,
TOP_AUD_INTBUS_SEL,
TOP_PMICSPI_SEL,
TOP_SCP_SEL,
TOP_ATB_SEL,
TOP_VENC_LT_SEL,
TOP_DPI0_SEL,
TOP_IRDA_SEL,
TOP_CCI400_SEL,
TOP_AUD_1_SEL,
TOP_AUD_2_SEL,
TOP_MEM_MFG_IN_SEL,
TOP_AXI_MFG_IN_SEL,
TOP_SCAM_SEL,
TOP_SPINFI_IFR_SEL,
TOP_HDMI_SEL,
TOP_DPILVDS_SEL,
TOP_MSDC50_2_H_SEL,
TOP_HDCP_SEL,
TOP_HDCP_24M_SEL,
TOP_RTC_SEL,
TOP_NR_MUX
};
#define MUX(_id, _reg, _mux_shift, _mux_width) \
[_id] = { \
.reg = &mtk_topckgen->_reg, \
.mux_shift = _mux_shift, \
.mux_width = _mux_width, \
.upd_reg = NULL, \
.upd_shift = 0, \
}
static const struct mux muxes[] = {
/* CLK_CFG_0 */
MUX(TOP_AXI_SEL, clk_cfg_0, 0, 3),
MUX(TOP_MEM_SEL, clk_cfg_0, 8, 1),
MUX(TOP_DDRPHYCFG_SEL, clk_cfg_0, 16, 1),
MUX(TOP_MM_SEL, clk_cfg_0, 24, 4),
/* CLK_CFG_1 */
MUX(TOP_PWM_SEL, clk_cfg_1, 0, 2),
MUX(TOP_VDEC_SEL, clk_cfg_1, 8, 4),
MUX(TOP_VENC_SEL, clk_cfg_1, 16, 4),
MUX(TOP_MFG_SEL, clk_cfg_1, 24, 4),
/* CLK_CFG_2 */
MUX(TOP_CAMTG_SEL, clk_cfg_2, 0, 3),
MUX(TOP_UART_SEL, clk_cfg_2, 8, 1),
MUX(TOP_SPI_SEL, clk_cfg_2, 16, 3),
MUX(TOP_USB20_SEL, clk_cfg_2, 24, 2),
/* CLK_CFG_3 */
MUX(TOP_USB30_SEL, clk_cfg_3, 0, 2),
MUX(TOP_MSDC50_0_H_SEL, clk_cfg_3, 8, 3),
MUX(TOP_MSDC50_0_SEL, clk_cfg_3, 16, 4),
MUX(TOP_MSDC30_1_SEL, clk_cfg_3, 24, 3),
/* CLK_CFG_4 */
MUX(TOP_MSDC30_2_SEL, clk_cfg_4, 0, 3),
MUX(TOP_MSDC30_3_SEL, clk_cfg_4, 8, 4),
MUX(TOP_AUDIO_SEL, clk_cfg_4, 16, 2),
MUX(TOP_AUD_INTBUS_SEL, clk_cfg_4, 24, 3),
/* CLK_CFG_5 */
MUX(TOP_PMICSPI_SEL, clk_cfg_5, 0, 3),
MUX(TOP_SCP_SEL, clk_cfg_5, 8, 3),
MUX(TOP_ATB_SEL, clk_cfg_5, 16, 2),
MUX(TOP_VENC_LT_SEL, clk_cfg_5, 24, 4),
/* CLK_CFG_6 */
MUX(TOP_DPI0_SEL, clk_cfg_6, 0, 3),
MUX(TOP_IRDA_SEL, clk_cfg_6, 8, 2),
MUX(TOP_CCI400_SEL, clk_cfg_6, 16, 3),
MUX(TOP_AUD_1_SEL, clk_cfg_6, 24, 2),
/* CLK_CFG_7 */
MUX(TOP_AUD_2_SEL, clk_cfg_7, 0, 2),
MUX(TOP_MEM_MFG_IN_SEL, clk_cfg_7, 8, 2),
MUX(TOP_AXI_MFG_IN_SEL, clk_cfg_7, 16, 2),
MUX(TOP_SCAM_SEL, clk_cfg_7, 24, 2),
/* CLK_CFG_12 */
MUX(TOP_SPINFI_IFR_SEL, clk_cfg_12, 0, 3),
MUX(TOP_HDMI_SEL, clk_cfg_12, 8, 2),
MUX(TOP_DPILVDS_SEL, clk_cfg_12, 24, 3),
/* CLK_CFG_13 */
MUX(TOP_MSDC50_2_H_SEL, clk_cfg_13, 0, 3),
MUX(TOP_HDCP_SEL, clk_cfg_13, 8, 2),
MUX(TOP_HDCP_24M_SEL, clk_cfg_13, 16, 2),
MUX(TOP_RTC_SEL, clk_cfg_13, 24, 2),
};
struct mux_sel {
enum mux_id id;
u32 sel;
};
static const struct mux_sel mux_sels[] = {
/* CLK_CFG_0 */
{ .id = TOP_AXI_SEL, .sel = 5 }, /* 5: univpll2_d2 */
{ .id = TOP_MEM_SEL, .sel = 0 }, /* 0: clk26m */
{ .id = TOP_DDRPHYCFG_SEL, .sel = 0 }, /* 0: clk26m */
{ .id = TOP_MM_SEL, .sel = 1 }, /* 1: vencpll_d2 */
/* CLK_CFG_1 */
{ .id = TOP_PWM_SEL, .sel = 0 }, /* 0: clk26m */
{ .id = TOP_VDEC_SEL, .sel = 1 }, /* 1: vcodecpll_ck */
{ .id = TOP_VENC_SEL, .sel = 1 }, /* 1: vcodecpll_ck */
{ .id = TOP_MFG_SEL, .sel = 1 }, /* 1: mmpll_ck */
/* CLK_CFG_2 */
{ .id = TOP_CAMTG_SEL, .sel = 0 }, /* 0: clk26m */
{ .id = TOP_UART_SEL, .sel = 0 }, /* 0: clk26m */
{ .id = TOP_SPI_SEL, .sel = 1 }, /* 1: syspll3_d2 */
{ .id = TOP_USB20_SEL, .sel = 1 }, /* 1: univpll1_d8 */
/* CLK_CFG_4 */
{ .id = TOP_MSDC30_2_SEL, .sel = 2 }, /* 2: msdcpll_d4 */
{ .id = TOP_MSDC30_3_SEL, .sel = 5 }, /* 5: msdcpll_d4 */
{ .id = TOP_AUDIO_SEL, .sel = 0 }, /* 0: clk26m */
{ .id = TOP_AUD_INTBUS_SEL, .sel = 1 }, /* 1: syspll1_d4 */
/* CLK_CFG_5 */
{ .id = TOP_PMICSPI_SEL, .sel = 0 }, /* 0: clk26m */
{ .id = TOP_SCP_SEL, .sel = 1 }, /* 1: syspll1_d2 */
{ .id = TOP_ATB_SEL, .sel = 0 }, /* 0: clk26m */
{ .id = TOP_VENC_LT_SEL, .sel = 6 }, /* 6: univpll1_d2 */
/* CLK_CFG_6 */
{ .id = TOP_DPI0_SEL, .sel = 1 }, /* 1: tvdpll_d2 */
{ .id = TOP_IRDA_SEL, .sel = 1 }, /* 1: univpll2_d4 */
{ .id = TOP_CCI400_SEL, .sel = 5 }, /* 5: syspll_d2 */
{ .id = TOP_AUD_1_SEL, .sel = 1 }, /* 1: apll1_ck */
/* CLK_CFG_7 */
{ .id = TOP_AUD_2_SEL, .sel = 1 }, /* 1: apll2_ck */
{ .id = TOP_MEM_MFG_IN_SEL, .sel = 1 }, /* 1: mmpll_ck */
{ .id = TOP_AXI_MFG_IN_SEL, .sel = 1 }, /* 1: hd_faxi_ck */
{ .id = TOP_SCAM_SEL, .sel = 1 }, /* 1: syspll3_d2 */
/* CLK_CFG_12 */
{ .id = TOP_SPINFI_IFR_SEL, .sel = 0 }, /* 0: clk26m */
{ .id = TOP_HDMI_SEL, .sel = 1 }, /* 1: AD_HDMITX_CLK */
{ .id = TOP_DPILVDS_SEL, .sel = 1 }, /* 1: AD_LVDSPLL_CK */
/* CLK_CFG_13 */
{ .id = TOP_MSDC50_2_H_SEL, .sel = 2 }, /* 2: syspll2_d2 */
{ .id = TOP_HDCP_SEL, .sel = 2 }, /* 2: syspll3_d4 */
{ .id = TOP_HDCP_24M_SEL, .sel = 2 }, /* 2: univpll_d52 */
{ .id = TOP_RTC_SEL, .sel = 1 }, /* 1: clkrtc_ext */
/* CLK_CFG_3 */
{ .id = TOP_USB30_SEL, .sel = 1 }, /* 1: univpll3_d2 */
{ .id = TOP_MSDC50_0_H_SEL, .sel = 2 }, /* 2: syspll2_d2 */
{ .id = TOP_MSDC50_0_SEL, .sel = 6 }, /* 6: msdcpll_d4 */
{ .id = TOP_MSDC30_1_SEL, .sel = 2 }, /* 2: msdcpll_d4 */
};
enum pll_id {
APMIXED_ARMCA15PLL,
APMIXED_ARMCA7PLL,
APMIXED_MAINPLL,
APMIXED_UNIVPLL,
APMIXED_MMPLL,
APMIXED_MSDCPLL,
APMIXED_VENCPLL,
APMIXED_TVDPLL,
APMIXED_MPLL,
APMIXED_VCODECPLL,
APMIXED_APLL1,
APMIXED_APLL2,
APMIXED_LVDSPLL,
APMIXED_MSDCPLL2,
APMIXED_NR_PLL
};
const u32 pll_div_rate[] = {
3UL * GHz,
1 * GHz,
500 * MHz,
250 * MHz,
125 * MHz,
0,
};
const u32 univpll_div_rate[] = {
3UL * GHz,
1500 * MHz,
750 * MHz,
375 * MHz,
187500 * KHz,
0,
};
const u32 mmpll_div_rate[] = {
3UL * GHz,
1 * GHz,
702 * MHz,
253500 * KHz,
126750 * KHz,
0,
};
static const struct pll plls[] = {
PLL(APMIXED_ARMCA15PLL, armca15pll_con0, armca15pll_pwr_con0,
NO_RSTB_SHIFT, 21, armca15pll_con1, 24, armca15pll_con1, 0,
pll_div_rate),
PLL(APMIXED_ARMCA7PLL, armca7pll_con0, armca7pll_pwr_con0,
PLL_RSTB_SHIFT, 21, armca7pll_con1, 24, armca7pll_con1, 0,
pll_div_rate),
PLL(APMIXED_MAINPLL, mainpll_con0, mainpll_pwr_con0,
PLL_RSTB_SHIFT, 21, mainpll_con0, 4, mainpll_con1, 0,
pll_div_rate),
PLL(APMIXED_UNIVPLL, univpll_con0, univpll_pwr_con0,
PLL_RSTB_SHIFT, 7, univpll_con0, 4, univpll_con1, 14,
univpll_div_rate),
PLL(APMIXED_MMPLL, mmpll_con0, mmpll_pwr_con0,
NO_RSTB_SHIFT, 21, mmpll_con1, 24, mmpll_con1, 0,
mmpll_div_rate),
PLL(APMIXED_MSDCPLL, msdcpll_con0, msdcpll_pwr_con0,
NO_RSTB_SHIFT, 21, msdcpll_con0, 4, msdcpll_con1, 0,
pll_div_rate),
PLL(APMIXED_VENCPLL, vencpll_con0, vencpll_pwr_con0,
NO_RSTB_SHIFT, 21, vencpll_con0, 4, vencpll_con1, 0,
pll_div_rate),
PLL(APMIXED_TVDPLL, tvdpll_con0, tvdpll_pwr_con0,
NO_RSTB_SHIFT, 21, tvdpll_con0, 4, tvdpll_con1, 0,
pll_div_rate),
PLL(APMIXED_MPLL, mpll_con0, mpll_pwr_con0,
NO_RSTB_SHIFT, 21, mpll_con0, 4, mpll_con1, 0,
pll_div_rate),
PLL(APMIXED_VCODECPLL, vcodecpll_con0, vcodecpll_pwr_con0,
NO_RSTB_SHIFT, 21, vcodecpll_con0, 4, vcodecpll_con1, 0,
pll_div_rate),
PLL(APMIXED_APLL1, apll1_con0, apll1_pwr_con0,
NO_RSTB_SHIFT, 31, apll1_con0, 4, apll1_con1, 0,
pll_div_rate),
PLL(APMIXED_APLL2, apll2_con0, apll2_pwr_con0,
NO_RSTB_SHIFT, 31, apll2_con0, 4, apll2_con1, 0,
pll_div_rate),
PLL(APMIXED_LVDSPLL, lvdspll_con0, lvdspll_pwr_con0,
NO_RSTB_SHIFT, 21, lvdspll_con0, 4, lvdspll_con1, 0,
pll_div_rate),
PLL(APMIXED_MSDCPLL2, msdcpll2_con0, msdcpll2_pwr_con0,
NO_RSTB_SHIFT, 21, msdcpll2_con0, 4, msdcpll2_con1, 0,
pll_div_rate),
};
struct rate {
enum pll_id id;
u32 rate;
};
static const struct rate rates[] = {
{ .id = APMIXED_ARMCA15PLL, .rate = ARMCA15PLL_HZ },
{ .id = APMIXED_ARMCA7PLL, .rate = ARMCA7PLL_HZ },
{ .id = APMIXED_MAINPLL, .rate = MAINPLL_HZ },
{ .id = APMIXED_UNIVPLL, .rate = UNIVPLL_HZ },
{ .id = APMIXED_MMPLL, .rate = MMPLL_HZ },
{ .id = APMIXED_MSDCPLL, .rate = MSDCPLL_HZ },
{ .id = APMIXED_VENCPLL, .rate = VENCPLL_HZ },
{ .id = APMIXED_TVDPLL, .rate = TVDPLL_HZ },
{ .id = APMIXED_MPLL, .rate = MPLL_HZ },
{ .id = APMIXED_VCODECPLL, .rate = VCODECPLL_HZ },
{ .id = APMIXED_LVDSPLL, .rate = LVDSPLL_HZ },
{ .id = APMIXED_MSDCPLL2, .rate = MSDCPLL2_HZ },
{ .id = APMIXED_APLL1, .rate = APLL1_HZ },
{ .id = APMIXED_APLL2, .rate = APLL2_HZ },
};
void pll_set_pcw_change(const struct pll *pll)
{
setbits32(pll->pcw_reg, PLL_PCW_CHG);
}
void mt_pll_init(void)
{
int i;
/* reduce CLKSQ disable time */
write32(&mtk_apmixed->clksq_stb_con0, (0x05 << 8) | (0x01 << 0));
/* extend PWR/ISO control timing to 1us */
write32(&mtk_apmixed->pll_iso_con0, (0x8 << 16) | (0x8 << 0));
write32(&mtk_apmixed->ap_pll_con6, 0x00000000);
/*************
* xPLL PWR ON
**************/
for (i = 0; i < APMIXED_NR_PLL; i++)
setbits32(plls[i].pwr_reg, PLL_PWR_ON);
/* wait for xPLL_PWR_ON ready (min delay is 1us) */
udelay(PLL_PWR_ON_DELAY);
/******************
* xPLL ISO Disable
*******************/
for (i = 0; i < APMIXED_NR_PLL; i++)
clrbits32(plls[i].pwr_reg, PLL_ISO);
/********************
* xPLL Frequency Set
*********************/
for (i = 0; i < ARRAY_SIZE(rates); i++)
pll_set_rate(&plls[rates[i].id], rates[i].rate);
/***********************
* xPLL Frequency Enable
************************/
for (i = 0; i < APMIXED_NR_PLL; i++)
setbits32(plls[i].reg, PLL_EN);
udelay(PLL_EN_DELAY); /* wait for PLL stable (min delay is 20us) */
/***************
* xPLL DIV RSTB
****************/
for (i = 0; i < APMIXED_NR_PLL; i++) {
if (plls[i].rstb_shift != NO_RSTB_SHIFT)
setbits32(plls[i].reg, 1 << plls[i].rstb_shift);
}
/**************
* INFRA CLKMUX
***************/
/* enable infrasys DCM */
setbits32(&mt8173_infracfg->top_dcmctl, 0x1);
write32(&mtk_topckgen->clk_mode, 0x1);
write32(&mtk_topckgen->clk_mode, 0x0); /* enable TOPCKGEN */
/************
* TOP CLKMUX -- DO NOT CHANGE WITHOUT ADJUSTING <soc/pll.h> CONSTANTS!
*************/
for (i = 0; i < ARRAY_SIZE(mux_sels); i++)
mux_set_sel(&muxes[mux_sels[i].id], mux_sels[i].sel);
/* enable scpsys clock off control */
write32(&mtk_topckgen->clk_scp_cfg_0,
(1 << 10) | (1 << 9) | (1 << 5) | (1 << 4) | (1 << 2) |
(1 << 1) | (1 << 0));
write32(&mtk_topckgen->clk_scp_cfg_1,
(1 << 4) | (1 << 2) | (1 << 0));
}
/* Turn on ADA_SSUSB_XTAL_CK 26MHz */
void mt_pll_enable_ssusb_clk(void)
{
/* set RG_LTECLKSQ_EN */
setbits32(&mtk_apmixed->ap_pll_con0, 0x1);
udelay(100); /* wait for PLL stable */
/* set RG_LTECLKSQ_LPF_EN & DA_REF2USB_TX_EN */
setbits32(&mtk_apmixed->ap_pll_con0, 0x1 << 1);
setbits32(&mtk_apmixed->ap_pll_con2, 0x1);
udelay(100); /* wait for PLL stable */
/* set DA_REF2USB_TX_LPF_EN & DA_REF2USB_TX_OUT_EN */
setbits32(&mtk_apmixed->ap_pll_con2, (0x1 << 2) | (0x1 << 1));
}
/* after pmic_init */
void mt_pll_post_init(void)
{
/* CPU clock divide by 1 */
clrbits32(&mt8173_infracfg->top_ckdiv1, 0x3ff);
/* select ARMPLL */
write32(&mt8173_infracfg->top_ckmuxsel, (1 << 2) | 1);
}
void mt_pll_set_aud_div(u32 rate)
{
u32 mclk_div;
u32 apll_clock = APLL2_CK_HZ;
int apll1 = 0;
if (rate % 11025 == 0) {
/* use APLL1 instead */
apll1 = 1;
apll_clock = APLL1_CK_HZ;
}
/* I2S1 clock */
mclk_div = (apll_clock / 256 / rate) - 1;
assert(apll_clock == rate * 256 * (mclk_div + 1));
if (apll1) {
/* mclk */
clrbits32(&mtk_topckgen->clk_auddiv_0, 1 << 5);
clrsetbits32(&mtk_topckgen->clk_auddiv_1, 0xff << 8,
mclk_div << 8);
/* bclk */
clrsetbits32(&mtk_topckgen->clk_auddiv_0, 0xf << 24,
7 << 24);
} else {
/* mclk */
setbits32(&mtk_topckgen->clk_auddiv_0, 1 << 5);
clrsetbits32(&mtk_topckgen->clk_auddiv_2, 0xff << 8,
mclk_div << 8);
/* bclk */
clrsetbits32(&mtk_topckgen->clk_auddiv_0, 0xf << 28,
7 << 28);
}
}
void mt_pll_raise_ca53_freq(u32 freq)
{
pll_set_rate(&plls[APMIXED_ARMCA7PLL], freq); /* freq in Hz */
}
void mt_mem_pll_config_pre(const struct mt8173_sdram_params *sdram_params)
{
u32 mpll_sdm_pcw_20_0 = 0xF13B1;
/* disable MPLL for adjusting memory clk frequency */
clrbits32(&mtk_apmixed->mpll_con0, BIT(0));
/* MPLL configuration: mode selection */
setbits32(&mtk_apmixed->mpll_con0, BIT(16));
clrbits32(&mtk_apmixed->mpll_con0, 0x7 << 4);
clrbits32(&mtk_apmixed->pll_test_con0, 1 << 31);
/* set RG_MPLL_SDM_PCW for feedback divide ratio */
clrsetbits32(&mtk_apmixed->mpll_con1, 0x1fffff, mpll_sdm_pcw_20_0);
}
void mt_mem_pll_config_post(void)
{
/* power up sequence starts: enable MPLL */
setbits32(&mtk_apmixed->mpll_con0, BIT(0));
}
void mt_mem_pll_mux(void)
{
/* CLK_CFG_0 */
mux_set_sel(&muxes[TOP_MEM_SEL], 1); /* 1: dmpll_ck */
}