static unsigned long clk_dfs_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_dfs *dfs = to_clk_dfs(hw); u32 mfn, mfi, rate; u32 dvport = readl_relaxed(DFS_DVPORTn(dfs->reg, dfs->idx)); mfn = (dvport & DFS_DVPORTn_MFN_MASK) >> DFS_DVPORTn_MFN_OFFSET; mfi = (dvport & DFS_DVPORTn_MFI_MASK) >> DFS_DVPORTn_MFI_OFFSET; mfi <<= 8; rate = parent_rate / (mfi + mfn); rate <<= 8; return rate; }
static int clk_dfs_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_dfs *dfs = to_clk_dfs(hw); u32 mfi; u32 portreset = readl_relaxed(DFS_PORTRESET(dfs->reg)); writel_relaxed( DFS_CTRL_DLL_RESET, DFS_CTRL(dfs->reg) ); writel_relaxed( portreset | ~DFS_PORTRESET_PORTRESET_SET(dfs->idx), DFS_PORTRESET(dfs->reg) ); mfi = parent_rate/rate; writel_relaxed( DFS_DVPORTn_MFI_SET(mfi) | DFS_DVPORTn_MFN_SET(dfs->mfn), DFS_DVPORTn(dfs->reg,dfs->idx) ); writel_relaxed( ~DFS_CTRL_DLL_RESET, DFS_CTRL(dfs->reg) ); while( readl_relaxed(DFS_PORTSR(dfs->reg)) & (1 << (dfs->idx)) ); return 0; }
static uintptr_t get_pllfreq(u32 pll, u32 refclk_freq, u32 plldv, u32 pllfd, u32 selected_output) { u32 vco = 0, plldv_prediv = 0, plldv_mfd = 0, pllfd_mfn = 0; u32 plldv_rfdphi_div = 0, fout = 0; u32 dfs_portn = 0, dfs_mfn = 0, dfs_mfi = 0; if (selected_output > DFS_MAXNUMBER) { return -1; } plldv_prediv = (plldv & PLLDIG_PLLDV_PREDIV_MASK) >> PLLDIG_PLLDV_PREDIV_OFFSET; plldv_mfd = (plldv & PLLDIG_PLLDV_MFD_MASK); pllfd_mfn = (pllfd & PLLDIG_PLLFD_MFN_MASK); plldv_prediv = plldv_prediv == 0 ? 1 : plldv_prediv; /* The formula for VCO is from TR manual, rev. D */ vco = refclk_freq / plldv_prediv * (plldv_mfd + pllfd_mfn / 20481); if (selected_output != 0) { /* Determine the RFDPHI for PHI1 */ plldv_rfdphi_div = (plldv & PLLDIG_PLLDV_RFDPHI1_MASK) >> PLLDIG_PLLDV_RFDPHI1_OFFSET; plldv_rfdphi_div = plldv_rfdphi_div == 0 ? 1 : plldv_rfdphi_div; if (pll == ARM_PLL || pll == ENET_PLL || pll == DDR_PLL) { dfs_portn = readl(DFS_DVPORTn(pll, selected_output - 1)); dfs_mfi = (dfs_portn & DFS_DVPORTn_MFI_MASK) >> DFS_DVPORTn_MFI_OFFSET; dfs_mfn = (dfs_portn & DFS_DVPORTn_MFI_MASK) >> DFS_DVPORTn_MFI_OFFSET; fout = vco / (dfs_mfi + (dfs_mfn / 256)); } else {
/* * Program the pll according to the input parameters. * pll - ARM_PLL, PERIPH_PLL, ENET_PLL, DDR_PLL, VIDEO_PLL. * refclk_freq - input reference clock frequency (FXOSC - 40 MHZ, FIRC - 48 MHZ) * freq - expected output frequency for PHY0 * freq1 - expected output frequency for PHY1 * dfs_nr - number of DFS modules for current PLL * dfs - array with the activation dfs field, mfn and mfi * plldv_prediv - divider of clkfreq_ref * plldv_mfd - loop multiplication factor divider * pllfd_mfn - numerator loop multiplication factor divider * Please consult the PLLDIG chapter of platform manual * before to use this function. *) */ static int program_pll(enum pll_type pll, u32 refclk_freq, u32 freq0, u32 freq1, u32 dfs_nr, u32 dfs[][DFS_PARAMS_Nr], u32 plldv_prediv, u32 plldv_mfd, u32 pllfd_mfn) { u32 i, rfdphi1, rfdphi, dfs_on = 0, fvco; /* * This formula is from platform reference manual (Rev. 1, 6/2015), PLLDIG chapter. */ fvco = (refclk_freq / plldv_prediv) * (plldv_mfd + pllfd_mfn / (float)20480); /* * VCO should have value in [ PLL_MIN_FREQ, PLL_MAX_FREQ ]. Please consult * the platform DataSheet in order to determine the allowed values. */ if (fvco < PLL_MIN_FREQ || fvco > PLL_MAX_FREQ) { return -1; } if (select_pll_source_clk(pll, refclk_freq) < 0) { return -1; } rfdphi = fvco / freq0; rfdphi1 = (freq1 == 0) ? 0 : fvco / freq1; writel(PLLDIG_PLLDV_RFDPHI1_SET(rfdphi1) | PLLDIG_PLLDV_RFDPHI_SET(rfdphi) | PLLDIG_PLLDV_PREDIV_SET(plldv_prediv) | PLLDIG_PLLDV_MFD(plldv_mfd), PLLDIG_PLLDV(pll)); writel(readl(PLLDIG_PLLFD(pll)) | PLLDIG_PLLFD_MFN_SET(pllfd_mfn) | PLLDIG_PLLFD_SMDEN, PLLDIG_PLLFD(pll)); /* switch on the pll in current mode */ writel(readl(MC_ME_RUNn_MC(0)) | MC_ME_RUNMODE_MC_PLL(pll), MC_ME_RUNn_MC(0)); entry_to_target_mode(MC_ME_MCTL_RUN0); /* Only ARM_PLL, ENET_PLL and DDR_PLL */ if ((pll == ARM_PLL) || (pll == ENET_PLL) || (pll == DDR_PLL)) { /* DFS clk enable programming */ writel(DFS_CTRL_DLL_RESET, DFS_CTRL(pll)); writel(DFS_DLLPRG1_CPICTRL_SET(0x5) | DFS_DLLPRG1_VSETTLCTRL_SET(0x1) | DFS_DLLPRG1_CALBYPEN_SET(0x0) | DFS_DLLPRG1_DACIN_SET(0x1) | DFS_DLLPRG1_LCKWT_SET(0x0) | DFS_DLLPRG1_V2IGC_SET(0x5), DFS_DLLPRG1(pll)); for (i = 0; i < dfs_nr; i++) { if (dfs[i][0]) { writel(DFS_DVPORTn_MFI_SET(dfs[i][2]) | DFS_DVPORTn_MFN_SET(dfs[i][1]), DFS_DVPORTn(pll, i)); dfs_on |= (dfs[i][0] << i); } } writel(readl(DFS_CTRL(pll)) & ~DFS_CTRL_DLL_RESET, DFS_CTRL(pll)); writel(readl(DFS_PORTRESET(pll)) & ~DFS_PORTRESET_PORTRESET_SET(dfs_on), DFS_PORTRESET(pll)); while ((readl(DFS_PORTSR(pll)) & dfs_on) != dfs_on) ; } entry_to_target_mode(MC_ME_MCTL_RUN0); return 0; }