/* SPDX-License-Identifier: GPL-2.0-only */ #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++) pll_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_little_cpu_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 */ pll_mux_set_sel(&muxes[TOP_MEM_SEL], 1); /* 1: dmpll_ck */ }