diff options
author | James Liao <jamesjj.liao@mediatek.com> | 2015-07-31 17:10:53 +0800 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2015-12-03 14:17:12 +0100 |
commit | f9fad12875d2ecbf47a3af5788f672eed406a9c5 (patch) | |
tree | dc1e6c1e78220be7577eb05094beeb7019f4c601 | |
parent | 3d7b6069e1c8e4e2054fd4648e8089f20a1990ff (diff) |
mediatek/mt8173: Add PLL driver
Add PLL init code.
BRANCH=none
BUG=none
TEST=none
Change-Id: I2dcea8cdea1a3812bd8b84b7e8d961e7f8d4d953
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: e6e2eecb2fad30db018685b61912103f5e2cd524
Original-Change-Id: Id67d8033f3b2a267a140d7d73daa5727bc032272
Original-Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/292670
Original-Commit-Ready: Yidi Lin <yidi.lin@mediatek.com>
Original-Tested-by: Yidi Lin <yidi.lin@mediatek.com>
Original-Reviewed-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://review.coreboot.org/12588
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
-rw-r--r-- | src/soc/mediatek/mt8173/Makefile.inc | 1 | ||||
-rw-r--r-- | src/soc/mediatek/mt8173/bootblock.c | 5 | ||||
-rw-r--r-- | src/soc/mediatek/mt8173/include/soc/pll.h | 291 | ||||
-rw-r--r-- | src/soc/mediatek/mt8173/pll.c | 460 |
4 files changed, 757 insertions, 0 deletions
diff --git a/src/soc/mediatek/mt8173/Makefile.inc b/src/soc/mediatek/mt8173/Makefile.inc index 74a7618c32..0d7957e347 100644 --- a/src/soc/mediatek/mt8173/Makefile.inc +++ b/src/soc/mediatek/mt8173/Makefile.inc @@ -21,6 +21,7 @@ ifeq ($(CONFIG_SOC_MEDIATEK_MT8173),y) bootblock-y += bootblock.c bootblock-y += cbfs.c +bootblock-y += pll.c bootblock-y += timer.c ifeq ($(CONFIG_BOOTBLOCK_CONSOLE),y) diff --git a/src/soc/mediatek/mt8173/bootblock.c b/src/soc/mediatek/mt8173/bootblock.c index 232a655583..aaa7f61cdb 100644 --- a/src/soc/mediatek/mt8173/bootblock.c +++ b/src/soc/mediatek/mt8173/bootblock.c @@ -18,7 +18,12 @@ */ #include <bootblock_common.h> +#include <soc/pll.h> void bootblock_soc_init(void) { + mt_pll_init(); + + /* post init pll */ + mt_pll_post_init(); } diff --git a/src/soc/mediatek/mt8173/include/soc/pll.h b/src/soc/mediatek/mt8173/include/soc/pll.h new file mode 100644 index 0000000000..b09ad41b18 --- /dev/null +++ b/src/soc/mediatek/mt8173/include/soc/pll.h @@ -0,0 +1,291 @@ +/* + * 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. + * + * 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 + */ + +#ifndef SOC_MEDIATEK_MT8173_PLL_H +#define SOC_MEDIATEK_MT8173_PLL_H + +#include <soc/addressmap.h> + +struct mt8173_topckgen_regs { + u32 clk_mode; + u32 dcm_cfg; + u32 reserved1[6]; + u32 tst_sel_0; /* 0x020 */ + u32 tst_sel_1; + u32 tst_sel_2; + u32 reserved2[5]; + u32 clk_cfg_0; /* 0x040 */ + u32 clk_cfg_0_set; + u32 clk_cfg_0_clr; + u32 reserved3[1]; + u32 clk_cfg_1; /* 0x050 */ + u32 clk_cfg_1_set; + u32 clk_cfg_1_clr; + u32 reserved4[1]; + u32 clk_cfg_2; /* 0x060 */ + u32 clk_cfg_2_set; + u32 clk_cfg_2_clr; + u32 reserved5[1]; + u32 clk_cfg_3; /* 0x070 */ + u32 clk_cfg_3_set; + u32 clk_cfg_3_clr; + u32 reserved6[1]; + u32 clk_cfg_4; /* 0x080 */ + u32 clk_cfg_4_set; + u32 clk_cfg_4_clr; + u32 reserved7[1]; + u32 clk_cfg_5; /* 0x090 */ + u32 clk_cfg_5_set; + u32 clk_cfg_5_clr; + u32 reserved8[1]; + u32 clk_cfg_6; /* 0x0a0 */ + u32 clk_cfg_6_set; + u32 clk_cfg_6_clr; + u32 reserved9[1]; + u32 clk_cfg_7; /* 0x0b0 */ + u32 clk_cfg_7_set; + u32 clk_cfg_7_clr; + u32 reserved10[1]; + u32 clk_cfg_12; /* 0x0c0 */ + u32 clk_cfg_12_set; + u32 clk_cfg_12_clr; + u32 reserved11[1]; + u32 clk_cfg_13; /* 0x0d0 */ + u32 clk_cfg_13_set; + u32 clk_cfg_13_clr; + u32 reserved12[9]; + u32 clk_cfg_8; /* 0x100 */ + u32 clk_cfg_9; + u32 clk_cfg_10; + u32 clk_cfg_11; + u32 reserved13[4]; + u32 clk_auddiv_0; /* 0x120 */ + u32 clk_auddiv_1; + u32 clk_auddiv_2; + u32 clk_auddiv_3; + u32 clk_mjcdiv_0; + u32 reserved14[51]; + u32 clk_scp_cfg_0; /* 0x200 */ + u32 clk_scp_cfg_1; + u32 reserved15[2]; + u32 clk_misc_cfg_0; /* 0x210 */ + u32 clk_misc_cfg_1; + u32 clk_misc_cfg_2; + u32 reserved16[1]; + u32 clk26cali_0; /* 0x220 */ + u32 clk26cali_1; + u32 clk26cali_2; + u32 cksta_reg; + u32 test_mode_cfg; + u32 reserved17[53]; + u32 mbist_cfg_0; /* 0x308 */ + u32 mbist_cfg_1; + u32 reset_deglitch_key; + u32 mbist_cfg_3; /* 0x314 */ +}; + +check_member(mt8173_topckgen_regs, clk_cfg_0, 0x40); +check_member(mt8173_topckgen_regs, clk_cfg_8, 0x100); +check_member(mt8173_topckgen_regs, clk_scp_cfg_0, 0x200); +check_member(mt8173_topckgen_regs, mbist_cfg_3, 0x314); + +struct mt8173_apmixed_regs { + u32 ap_pll_con0; + u32 reserved1[1]; + u32 ap_pll_con2; /* 0x008 */ + u32 ap_pll_con3; + u32 ap_pll_con4; + u32 ap_pll_con5; + u32 ap_pll_con6; + u32 ap_pll_con7; + u32 clksq_stb_con0; + u32 pll_pwr_con0; + u32 pll_pwr_con1; + u32 pll_iso_con0; + u32 pll_iso_con1; + u32 pll_stb_con0; + u32 div_stb_con0; + u32 pll_chg_con0; + u32 pll_test_con0; + u32 pll_test_con1; /* 0x044 */ + u32 reserved2[110]; + u32 armca15pll_con0; /* 0x200 */ + u32 armca15pll_con1; + u32 armca15pll_con2; + u32 armca15pll_pwr_con0; + u32 armca7pll_con0; + u32 armca7pll_con1; + u32 armca7pll_con2; + u32 armca7pll_pwr_con0; + u32 mainpll_con0; + u32 mainpll_con1; + u32 mainpll_con2; + u32 mainpll_pwr_con0; + u32 univpll_con0; + u32 univpll_con1; + u32 univpll_con2; + u32 univpll_pwr_con0; + u32 mmpll_con0; + u32 mmpll_con1; + u32 mmpll_con2; + u32 mmpll_pwr_con0; + u32 msdcpll_con0; + u32 msdcpll_con1; + u32 msdcpll_con2; + u32 msdcpll_pwr_con0; + u32 vencpll_con0; + u32 vencpll_con1; + u32 vencpll_con2; + u32 vencpll_pwr_con0; + u32 tvdpll_con0; + u32 tvdpll_con1; + u32 tvdpll_con2; + u32 tvdpll_pwr_con0; + u32 mpll_con0; + u32 mpll_con1; + u32 mpll_con2; + u32 mpll_pwr_con0; + u32 vcodecpll_con0; + u32 vcodecpll_con1; + u32 vcodecpll_con2; + u32 vcodecpll_pwr_con0; + u32 apll1_con0; + u32 apll1_con1; + u32 apll1_con2; + u32 apll1_con3; + u32 apll1_pwr_con0; + u32 apll2_con0; + u32 apll2_con1; + u32 apll2_con2; + u32 apll2_con3; + u32 apll2_pwr_con0; + u32 reserved3[2]; + u32 lvdspll_con0; /* 0x2d0 */ + u32 lvdspll_con1; + u32 lvdspll_con2; + u32 lvdspll_pwr_con0; + u32 lvdspll_ssc_con0; + u32 lvdspll_ssc_con1; + u32 lvdspll_ssc_con2; + u32 reserved4[1]; + u32 msdcpll2_con0; /* 0x2f0 */ + u32 msdcpll2_con1; + u32 msdcpll2_con2; + u32 msdcpll2_pwr_con0; /* 0x2fc */ +}; + +check_member(mt8173_apmixed_regs, ap_pll_con2, 0x8); +check_member(mt8173_apmixed_regs, armca15pll_con0, 0x200); +check_member(mt8173_apmixed_regs, msdcpll2_pwr_con0, 0x2fc); + +static struct mt8173_topckgen_regs *const mt8173_topckgen = (void *)CKSYS_BASE; +static struct mt8173_apmixed_regs *const mt8173_apmixed = (void *)APMIXED_BASE; + +/* PLL rate */ +enum { + ARMCA15PLL_HZ = 851500 * KHz, + ARMCA7PLL_HZ = 1105 * MHz, + MAINPLL_HZ = 1092 * MHz, + UNIVPLL_HZ = 1248 * MHz, + MMPLL_HZ = 455 * MHz, + MSDCPLL_HZ = 800 * MHz, + VENCPLL_HZ = 660 * MHz, + TVDPLL_HZ = 1782 * MHz, + MPLL_HZ = 1456 * MHz, + VCODECPLL_HZ = 1104 * MHz, + LVDSPLL_HZ = 150 * MHz, + MSDCPLL2_HZ = 800 * MHz, + APLL1_HZ = 180633600, + APLL2_HZ = 196608 * KHz, +}; + +/* top_div rate */ +enum { + AD_HDMITX_CLK_HZ = TVDPLL_HZ / 12, + AD_LVDSPLL_CK_HZ = LVDSPLL_HZ, + APLL1_CK_HZ = APLL1_HZ, + APLL2_CK_HZ = APLL2_HZ, + CLK26M_HZ = 26 * MHz, + CLKRTC_EXT_HZ = 32 * KHz, + MMPLL_CK_HZ = MMPLL_HZ, + MSDCPLL_D4_HZ = MSDCPLL_HZ / 4, + SYSPLL1_D2_HZ = MAINPLL_HZ / 4, + SYSPLL1_D4_HZ = MAINPLL_HZ / 8, + SYSPLL2_D2_HZ = MAINPLL_HZ / 6, + SYSPLL3_D2_HZ = MAINPLL_HZ / 10, + SYSPLL3_D4_HZ = MAINPLL_HZ / 20, + SYSPLL_D2_HZ = MAINPLL_HZ / 2, + TVDPLL_D2_HZ = TVDPLL_HZ / 2, + UNIVPLL1_D2_HZ = UNIVPLL_HZ / 4, + UNIVPLL1_D8_HZ = UNIVPLL_HZ / 16, + UNIVPLL2_D2_HZ = UNIVPLL_HZ / 6, + UNIVPLL2_D4_HZ = UNIVPLL_HZ / 12, + UNIVPLL3_D2_HZ = UNIVPLL_HZ / 10, + UNIVPLL_D52_HZ = UNIVPLL_HZ / 52, + VCODECPLL_CK_HZ = VCODECPLL_HZ / 3, + VENCPLL_D2_HZ = VENCPLL_HZ / 2, +}; + +/* top_mux rate */ +enum { + AXI_HZ = UNIVPLL2_D2_HZ, + MEM_HZ = CLK26M_HZ, + DDRPHYCFG_HZ = CLK26M_HZ, + MM_HZ = VENCPLL_D2_HZ, + PWM_HZ = CLK26M_HZ, + VDEC_HZ = VCODECPLL_CK_HZ, + VENC_HZ = VCODECPLL_CK_HZ, + MFG_HZ = MMPLL_CK_HZ, + CAMTG_HZ = CLK26M_HZ, + UART_HZ = CLK26M_HZ, + SPI_HZ = SYSPLL3_D2_HZ, + USB20_HZ = UNIVPLL1_D8_HZ, + MSDC30_2_HZ = MSDCPLL_D4_HZ, + MSDC30_3_HZ = MSDCPLL_D4_HZ, + AUDIO_HZ = CLK26M_HZ, + AUD_INTBUS_HZ = SYSPLL1_D4_HZ, + PMICSPI_HZ = CLK26M_HZ, + SCP_HZ = SYSPLL1_D2_HZ, + ATB_HZ = CLK26M_HZ, + VENC_LT_HZ = UNIVPLL1_D2_HZ, + DPI0_HZ = TVDPLL_D2_HZ, + IRDA_HZ = UNIVPLL2_D4_HZ, + CCI400_HZ = SYSPLL_D2_HZ, + AUD_1_HZ = APLL1_CK_HZ, + AUD_2_HZ = APLL2_CK_HZ, + MEM_MFG_IN_HZ = MMPLL_CK_HZ, + AXI_MFG_IN_HZ = AXI_HZ, + SCAM_HZ = SYSPLL3_D2_HZ, + SPINFI_IFR_HZ = CLK26M_HZ, + HDMI_HZ = AD_HDMITX_CLK_HZ, + DPILVDS_HZ = AD_LVDSPLL_CK_HZ, + MSDC50_2_H_HZ = SYSPLL2_D2_HZ, + HDCP_HZ = SYSPLL3_D4_HZ, + HDCP_24M_HZ = UNIVPLL_D52_HZ, + RTC_HZ = CLKRTC_EXT_HZ, + USB30_HZ = UNIVPLL3_D2_HZ, + MSDC50_0_H_HZ = SYSPLL2_D2_HZ, + MSDC50_0_HZ = MSDCPLL_D4_HZ, + MSDC30_1_HZ = MSDCPLL_D4_HZ, +}; + +void mt_pll_post_init(void); +void mt_pll_init(void); + +#endif /* SOC_MEDIATEK_MT8173_PLL_H */ diff --git a/src/soc/mediatek/mt8173/pll.c b/src/soc/mediatek/mt8173/pll.c new file mode 100644 index 0000000000..f1ea9a9f03 --- /dev/null +++ b/src/soc/mediatek/mt8173/pll.c @@ -0,0 +1,460 @@ +/* + * 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. + * + * 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 + */ + +#include <arch/io.h> +#include <assert.h> +#include <console/console.h> +#include <delay.h> +#include <stddef.h> + +#include <soc/addressmap.h> +#include <soc/infracfg.h> +#include <soc/pll.h> + +#define GENMASK(h, l) (((1U << ((h) - (l) + 1)) - 1) << (l)) + +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 TOPCKGEN_REG(x) (CKSYS_BASE + offsetof(struct mt8173_topckgen_regs, x)) +#define APMIXED_REG(x) (APMIXED_BASE + offsetof(struct mt8173_apmixed_regs, x)) + +struct mux { + void *reg; + u8 mux_shift; + u8 mux_width; +}; + +#define MUX(_id, _reg, _mux_shift, _mux_width) \ + [_id] = { \ + .reg = (void *)TOPCKGEN_REG(_reg), \ + .mux_shift = _mux_shift, \ + .mux_width = _mux_width, \ + } + +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), +}; + +static void mux_set_sel(const struct mux *mux, u32 sel) +{ + u32 mask = GENMASK(mux->mux_width - 1, 0); + u32 val = read32(mux->reg); + + val &= ~(mask << mux->mux_shift); + val |= (sel & mask) << mux->mux_shift; + write32(mux->reg, val); +} + +#define PLL_PWR_ON (1 << 0) +#define PLL_EN (1 << 0) +#define PLL_ISO (1 << 1) +#define PLL_RSTB (1 << 24) +#define PLL_PCW_CHG (1 << 31) +#define PLL_POSTDIV_MASK 0x7 +#define PCW_INTEGER_BITS 7 + +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, +}; + +struct pll { + void *reg; + void *pwr_reg; + u32 rstb; + u8 pcwbits; + void *div_reg; + u8 div_shift; + void *pcw_reg; + u8 pcw_shift; + const u32 *div_rate; +}; + +#define PLL(_id, _reg, _pwr_reg, _rstb, _pcwbits, _div_reg, _div_shift, \ + _pcw_reg, _pcw_shift, _div_rate) \ + [_id] = { \ + .reg = (void *)APMIXED_REG(_reg), \ + .pwr_reg = (void *)APMIXED_REG(_pwr_reg), \ + .rstb = _rstb, \ + .pcwbits = _pcwbits, \ + .div_reg = (void *)APMIXED_REG(_div_reg), \ + .div_shift = _div_shift, \ + .pcw_reg = (void *)APMIXED_REG(_pcw_reg), \ + .pcw_shift = _pcw_shift, \ + .div_rate = _div_rate, \ + } + +static const struct pll plls[] = { + PLL(APMIXED_ARMCA15PLL, armca15pll_con0, armca15pll_pwr_con0, 0, 21, + armca15pll_con1, 24, armca15pll_con1, 0, pll_div_rate), + PLL(APMIXED_ARMCA7PLL, armca7pll_con0, armca7pll_pwr_con0, PLL_RSTB, 21, + armca7pll_con1, 24, armca7pll_con1, 0, pll_div_rate), + PLL(APMIXED_MAINPLL, mainpll_con0, mainpll_pwr_con0, PLL_RSTB, 21, + mainpll_con0, 4, mainpll_con1, 0, pll_div_rate), + PLL(APMIXED_UNIVPLL, univpll_con0, univpll_pwr_con0, PLL_RSTB, 7, + univpll_con0, 4, univpll_con1, 14, univpll_div_rate), + PLL(APMIXED_MMPLL, mmpll_con0, mmpll_pwr_con0, 0, 21, + mmpll_con1, 24, mmpll_con1, 0, mmpll_div_rate), + PLL(APMIXED_MSDCPLL, msdcpll_con0, msdcpll_pwr_con0, 0, 21, + msdcpll_con0, 4, msdcpll_con1, 0, pll_div_rate), + PLL(APMIXED_VENCPLL, vencpll_con0, vencpll_pwr_con0, 0, 21, + vencpll_con0, 4, vencpll_con1, 0, pll_div_rate), + PLL(APMIXED_TVDPLL, tvdpll_con0, tvdpll_pwr_con0, 0, 21, + tvdpll_con0, 4, tvdpll_con1, 0, pll_div_rate), + PLL(APMIXED_MPLL, mpll_con0, mpll_pwr_con0, 0, 21, + mpll_con0, 4, mpll_con1, 0, pll_div_rate), + PLL(APMIXED_VCODECPLL, vcodecpll_con0, vcodecpll_pwr_con0, 0, 21, + vcodecpll_con0, 4, vcodecpll_con1, 0, pll_div_rate), + PLL(APMIXED_APLL1, apll1_con0, apll1_pwr_con0, 0, 31, + apll1_con0, 4, apll1_con1, 0, pll_div_rate), + PLL(APMIXED_APLL2, apll2_con0, apll2_pwr_con0, 0, 31, + apll2_con0, 4, apll2_con1, 0, pll_div_rate), + PLL(APMIXED_LVDSPLL, lvdspll_con0, lvdspll_pwr_con0, 0, 21, + lvdspll_con0, 4, lvdspll_con1, 0, pll_div_rate), + PLL(APMIXED_MSDCPLL2, msdcpll2_con0, msdcpll2_pwr_con0, 0, 21, + msdcpll2_con0, 4, msdcpll2_con1, 0, pll_div_rate), +}; + +static void pll_set_rate_regs(const struct pll *pll, u32 pcw, u32 postdiv) +{ + u32 val; + + /* set postdiv */ + val = read32(pll->div_reg); + val &= ~(PLL_POSTDIV_MASK << pll->div_shift); + val |= postdiv << pll->div_shift; + + /* postdiv and pcw need to set at the same time if on same register */ + if (pll->div_reg != pll->pcw_reg) { + write32(pll->div_reg, val); + val = read32(pll->pcw_reg); + } + + /* set pcw */ + val &= ~GENMASK(pll->pcw_shift + pll->pcwbits - 1, pll->pcw_shift); + val |= pcw << pll->pcw_shift; + val |= PLL_PCW_CHG; + write32(pll->pcw_reg, val); +} + +static void pll_calc_values(const struct pll *pll, u32 *pcw, u32 *postdiv, + u32 freq) +{ + const u32 fin_hz = CLK26M_HZ; + const u32 *div_rate = pll->div_rate; + u32 val; + + assert(freq <= div_rate[0]); + assert(freq >= 1 * GHz / 16); + + for (val = 1; div_rate[val] != 0; val++) { + if (freq > div_rate[val]) + break; + } + val--; + *postdiv = val; + + /* _pcw = freq * 2^postdiv / fin * 2^pcwbits_fractional */ + val += pll->pcwbits - PCW_INTEGER_BITS; + + *pcw = ((u64)freq << val) / fin_hz; +} + +static int pll_set_rate(const struct pll *pll, u32 rate) +{ + u32 pcw = 0; + u32 postdiv; + + pll_calc_values(pll, &pcw, &postdiv, rate); + pll_set_rate_regs(pll, pcw, postdiv); + + return 0; +} + +void mt_pll_init(void) +{ + int i; + + /* reduce CLKSQ disable time */ + write32(&mt8173_apmixed->clksq_stb_con0, (0x05 << 8) | (0x01 << 0)); + /* extend PWR/ISO control timing to 1us */ + write32(&mt8173_apmixed->pll_iso_con0, (0x8 << 16) | (0x8 << 0)); + write32(&mt8173_apmixed->ap_pll_con6, 0x00000000); + + /************* + * xPLL PWR ON + **************/ + for (i = 0; i < APMIXED_NR_PLL; i++) + setbits_le32(plls[i].pwr_reg, PLL_PWR_ON); + + udelay(5); /* wait for xPLL_PWR_ON ready (min delay is 1us) */ + + /****************** + * xPLL ISO Disable + *******************/ + for (i = 0; i < APMIXED_NR_PLL; i++) + clrbits_le32(plls[i].pwr_reg, PLL_ISO); + + /******************** + * xPLL Frequency Set + *********************/ + + pll_set_rate(&plls[APMIXED_ARMCA15PLL], ARMCA15PLL_HZ); + pll_set_rate(&plls[APMIXED_ARMCA7PLL], ARMCA7PLL_HZ); + pll_set_rate(&plls[APMIXED_MAINPLL], MAINPLL_HZ); + pll_set_rate(&plls[APMIXED_UNIVPLL], UNIVPLL_HZ); + pll_set_rate(&plls[APMIXED_MMPLL], MMPLL_HZ); + pll_set_rate(&plls[APMIXED_MSDCPLL], MSDCPLL_HZ); + pll_set_rate(&plls[APMIXED_VENCPLL], VENCPLL_HZ); + pll_set_rate(&plls[APMIXED_TVDPLL], TVDPLL_HZ); + pll_set_rate(&plls[APMIXED_MPLL], MPLL_HZ); + pll_set_rate(&plls[APMIXED_VCODECPLL], VCODECPLL_HZ); + pll_set_rate(&plls[APMIXED_LVDSPLL], LVDSPLL_HZ); + pll_set_rate(&plls[APMIXED_MSDCPLL2], MSDCPLL2_HZ); + pll_set_rate(&plls[APMIXED_APLL1], APLL1_HZ); + pll_set_rate(&plls[APMIXED_APLL2], APLL2_HZ); + + /*********************** + * xPLL Frequency Enable + ************************/ + for (i = 0; i < APMIXED_NR_PLL; i++) + setbits_le32(plls[i].reg, PLL_EN); + + udelay(40); /* wait for PLL stable (min delay is 20us) */ + + /*************** + * xPLL DIV RSTB + ****************/ + for (i = 0; i < APMIXED_NR_PLL; i++) { + if (plls[i].rstb) + setbits_le32(plls[i].reg, plls[i].rstb); + } + + /************** + * INFRA CLKMUX + ***************/ + + /* enable infrasys DCM */ + setbits_le32(&mt8173_infracfg->top_dcmctl, 0x1); + + write32(&mt8173_topckgen->clk_mode, 0x1); + write32(&mt8173_topckgen->clk_mode, 0x0); /* enable TOPCKGEN */ + + /************ + * TOP CLKMUX -- DO NOT CHANGE WITHOUT ADJUSTING <soc/pll.h> CONSTANTS! + *************/ + + /* CLK_CFG_0 */ + mux_set_sel(&muxes[TOP_AXI_SEL], 5); /* 5: univpll2_d2 */ + mux_set_sel(&muxes[TOP_MEM_SEL], 0); /* 0: clk26m */ + mux_set_sel(&muxes[TOP_DDRPHYCFG_SEL], 0); /* 0: clk26m */ + mux_set_sel(&muxes[TOP_MM_SEL], 1); /* 1: vencpll_d2 */ + /* CLK_CFG_1 */ + mux_set_sel(&muxes[TOP_PWM_SEL], 0); /* 0: clk26m */ + mux_set_sel(&muxes[TOP_VDEC_SEL], 1); /* 1: vcodecpll_ck */ + mux_set_sel(&muxes[TOP_VENC_SEL], 1); /* 1: vcodecpll_ck */ + mux_set_sel(&muxes[TOP_MFG_SEL], 1); /* 1: mmpll_ck */ + /* CLK_CFG_2 */ + mux_set_sel(&muxes[TOP_CAMTG_SEL], 0); /* 0: clk26m */ + mux_set_sel(&muxes[TOP_UART_SEL], 0); /* 0: clk26m */ + mux_set_sel(&muxes[TOP_SPI_SEL], 1); /* 1: syspll3_d2 */ + mux_set_sel(&muxes[TOP_USB20_SEL], 1); /* 1: univpll1_d8 */ + /* CLK_CFG_4 */ + mux_set_sel(&muxes[TOP_MSDC30_2_SEL], 2); /* 2: msdcpll_d4 */ + mux_set_sel(&muxes[TOP_MSDC30_3_SEL], 5); /* 5: msdcpll_d4 */ + mux_set_sel(&muxes[TOP_AUDIO_SEL], 0); /* 0: clk26m */ + mux_set_sel(&muxes[TOP_AUD_INTBUS_SEL], 1); /* 1: syspll1_d4 */ + /* CLK_CFG_5 */ + mux_set_sel(&muxes[TOP_PMICSPI_SEL], 0); /* 0: clk26m */ + mux_set_sel(&muxes[TOP_SCP_SEL], 1); /* 1: syspll1_d2 */ + mux_set_sel(&muxes[TOP_ATB_SEL], 0); /* 0: clk26m */ + mux_set_sel(&muxes[TOP_VENC_LT_SEL], 6); /* 6: univpll1_d2 */ + /* CLK_CFG_6 */ + mux_set_sel(&muxes[TOP_DPI0_SEL], 1); /* 1: tvdpll_d2 */ + mux_set_sel(&muxes[TOP_IRDA_SEL], 1); /* 1: univpll2_d4 */ + mux_set_sel(&muxes[TOP_CCI400_SEL], 5); /* 5: syspll_d2 */ + mux_set_sel(&muxes[TOP_AUD_1_SEL], 1); /* 1: apll1_ck */ + /* CLK_CFG_7 */ + mux_set_sel(&muxes[TOP_AUD_2_SEL], 1); /* 1: apll2_ck */ + mux_set_sel(&muxes[TOP_MEM_MFG_IN_SEL], 1); /* 1: mmpll_ck */ + mux_set_sel(&muxes[TOP_AXI_MFG_IN_SEL], 1); /* 1: hd_faxi_ck */ + mux_set_sel(&muxes[TOP_SCAM_SEL], 1); /* 1: syspll3_d2 */ + /* CLK_CFG_12 */ + mux_set_sel(&muxes[TOP_SPINFI_IFR_SEL], 0); /* 0: clk26m */ + mux_set_sel(&muxes[TOP_HDMI_SEL], 1); /* 1: AD_HDMITX_CLK */ + mux_set_sel(&muxes[TOP_DPILVDS_SEL], 1); /* 1: AD_LVDSPLL_CK */ + /* CLK_CFG_13 */ + mux_set_sel(&muxes[TOP_MSDC50_2_H_SEL], 2); /* 2: syspll2_d2 */ + mux_set_sel(&muxes[TOP_HDCP_SEL], 2); /* 2: syspll3_d4 */ + mux_set_sel(&muxes[TOP_HDCP_24M_SEL], 2); /* 2: univpll_d52 */ + mux_set_sel(&muxes[TOP_RTC_SEL], 1); /* 1: clkrtc_ext */ + /* CLK_CFG_3 */ + mux_set_sel(&muxes[TOP_USB30_SEL], 1); /* 1: univpll3_d2 */ + mux_set_sel(&muxes[TOP_MSDC50_0_H_SEL], 2); /* 2: syspll2_d2 */ + mux_set_sel(&muxes[TOP_MSDC50_0_SEL], 6); /* 6: msdcpll_d4 */ + mux_set_sel(&muxes[TOP_MSDC30_1_SEL], 2); /* 2: msdcpll_d4 */ + + /* enable scpsys clock off control */ + write32(&mt8173_topckgen->clk_scp_cfg_0, + (1 << 10) | (1 << 9) | (1 << 5) | (1 << 4) | (1 << 2) | + (1 << 1) | (1 << 0)); + write32(&mt8173_topckgen->clk_scp_cfg_1, + (1 << 4) | (1 << 2) | (1 << 0)); +} + +/* after pmic_init */ +void mt_pll_post_init(void) +{ + /* CPU clock divide by 1 */ + clrbits_le32(&mt8173_infracfg->top_ckdiv1, 0x3ff); + + /* select ARMPLL */ + /* TODO: possibly raise ARMPLL frequency here */ + /* NOTICE: raise Vproc voltage before raise ARMPLL frequency */ + write32(&mt8173_infracfg->top_ckmuxsel, (1 << 2) | 1); +} |