Eddy Petrișor | 5178dc1 | 2016-06-05 03:43:00 +0300 | [diff] [blame] | 1 | /* |
| 2 | * (C) Copyright 2015, Freescale Semiconductor, Inc. |
| 3 | * |
| 4 | * SPDX-License-Identifier: GPL-2.0+ |
| 5 | */ |
| 6 | |
| 7 | #include <asm/io.h> |
| 8 | #include <asm/arch/imx-regs.h> |
| 9 | #include <asm/arch/mc_cgm_regs.h> |
| 10 | #include <asm/arch/mc_me_regs.h> |
| 11 | #include <asm/arch/clock.h> |
| 12 | |
| 13 | /* |
| 14 | * Select the clock reference for required pll. |
| 15 | * pll - ARM_PLL, PERIPH_PLL, ENET_PLL, DDR_PLL, VIDEO_PLL. |
| 16 | * refclk_freq - input referece clock frequency (FXOSC - 40 MHZ, FIRC - 48 MHZ) |
| 17 | */ |
| 18 | static int select_pll_source_clk(enum pll_type pll, u32 refclk_freq) |
| 19 | { |
| 20 | u32 clk_src; |
| 21 | u32 pll_idx; |
| 22 | volatile struct src *src = (struct src *)SRC_SOC_BASE_ADDR; |
| 23 | |
| 24 | /* select the pll clock source */ |
| 25 | switch (refclk_freq) { |
| 26 | case FIRC_CLK_FREQ: |
| 27 | clk_src = SRC_GPR1_FIRC_CLK_SOURCE; |
| 28 | break; |
| 29 | case XOSC_CLK_FREQ: |
| 30 | clk_src = SRC_GPR1_XOSC_CLK_SOURCE; |
| 31 | break; |
| 32 | default: |
| 33 | /* The clock frequency for the source clock is unknown */ |
| 34 | return -1; |
| 35 | } |
| 36 | /* |
| 37 | * The hardware definition is not uniform, it has to calculate again |
| 38 | * the recurrence formula. |
| 39 | */ |
| 40 | switch (pll) { |
| 41 | case PERIPH_PLL: |
| 42 | pll_idx = 3; |
| 43 | break; |
| 44 | case ENET_PLL: |
| 45 | pll_idx = 1; |
| 46 | break; |
| 47 | case DDR_PLL: |
| 48 | pll_idx = 2;; |
| 49 | break; |
| 50 | default: |
| 51 | pll_idx = pll; |
| 52 | } |
| 53 | |
| 54 | writel(readl(&src->gpr1) | SRC_GPR1_PLL_SOURCE(pll_idx, clk_src), |
| 55 | &src->gpr1); |
| 56 | |
| 57 | return 0; |
| 58 | } |
| 59 | |
| 60 | static void entry_to_target_mode(u32 mode) |
| 61 | { |
| 62 | writel(mode | MC_ME_MCTL_KEY, MC_ME_MCTL); |
| 63 | writel(mode | MC_ME_MCTL_INVERTEDKEY, MC_ME_MCTL); |
| 64 | while ((readl(MC_ME_GS) & MC_ME_GS_S_MTRANS) != 0x00000000) ; |
| 65 | } |
| 66 | |
| 67 | /* |
| 68 | * Program the pll according to the input parameters. |
| 69 | * pll - ARM_PLL, PERIPH_PLL, ENET_PLL, DDR_PLL, VIDEO_PLL. |
| 70 | * refclk_freq - input reference clock frequency (FXOSC - 40 MHZ, FIRC - 48 MHZ) |
| 71 | * freq - expected output frequency for PHY0 |
| 72 | * freq1 - expected output frequency for PHY1 |
| 73 | * dfs_nr - number of DFS modules for current PLL |
| 74 | * dfs - array with the activation dfs field, mfn and mfi |
| 75 | * plldv_prediv - divider of clkfreq_ref |
| 76 | * plldv_mfd - loop multiplication factor divider |
| 77 | * pllfd_mfn - numerator loop multiplication factor divider |
| 78 | * Please consult the PLLDIG chapter of platform manual |
| 79 | * before to use this function. |
| 80 | *) |
| 81 | */ |
| 82 | static int program_pll(enum pll_type pll, u32 refclk_freq, u32 freq0, u32 freq1, |
| 83 | u32 dfs_nr, u32 dfs[][DFS_PARAMS_Nr], u32 plldv_prediv, |
| 84 | u32 plldv_mfd, u32 pllfd_mfn) |
| 85 | { |
| 86 | u32 i, rfdphi1, rfdphi, dfs_on = 0, fvco; |
| 87 | |
| 88 | /* |
| 89 | * This formula is from platform reference manual (Rev. 1, 6/2015), PLLDIG chapter. |
| 90 | */ |
| 91 | fvco = |
| 92 | (refclk_freq / plldv_prediv) * (plldv_mfd + |
| 93 | pllfd_mfn / (float)20480); |
| 94 | |
| 95 | /* |
| 96 | * VCO should have value in [ PLL_MIN_FREQ, PLL_MAX_FREQ ]. Please consult |
| 97 | * the platform DataSheet in order to determine the allowed values. |
| 98 | */ |
| 99 | |
| 100 | if (fvco < PLL_MIN_FREQ || fvco > PLL_MAX_FREQ) { |
| 101 | return -1; |
| 102 | } |
| 103 | |
| 104 | if (select_pll_source_clk(pll, refclk_freq) < 0) { |
| 105 | return -1; |
| 106 | } |
| 107 | |
| 108 | rfdphi = fvco / freq0; |
| 109 | |
| 110 | rfdphi1 = (freq1 == 0) ? 0 : fvco / freq1; |
| 111 | |
| 112 | writel(PLLDIG_PLLDV_RFDPHI1_SET(rfdphi1) | |
| 113 | PLLDIG_PLLDV_RFDPHI_SET(rfdphi) | |
| 114 | PLLDIG_PLLDV_PREDIV_SET(plldv_prediv) | |
| 115 | PLLDIG_PLLDV_MFD(plldv_mfd), PLLDIG_PLLDV(pll)); |
| 116 | |
| 117 | writel(readl(PLLDIG_PLLFD(pll)) | PLLDIG_PLLFD_MFN_SET(pllfd_mfn) | |
| 118 | PLLDIG_PLLFD_SMDEN, PLLDIG_PLLFD(pll)); |
| 119 | |
| 120 | /* switch on the pll in current mode */ |
| 121 | writel(readl(MC_ME_RUNn_MC(0)) | MC_ME_RUNMODE_MC_PLL(pll), |
| 122 | MC_ME_RUNn_MC(0)); |
| 123 | |
| 124 | entry_to_target_mode(MC_ME_MCTL_RUN0); |
| 125 | |
| 126 | /* Only ARM_PLL, ENET_PLL and DDR_PLL */ |
| 127 | if ((pll == ARM_PLL) || (pll == ENET_PLL) || (pll == DDR_PLL)) { |
| 128 | /* DFS clk enable programming */ |
| 129 | writel(DFS_CTRL_DLL_RESET, DFS_CTRL(pll)); |
| 130 | |
| 131 | writel(DFS_DLLPRG1_CPICTRL_SET(0x5) | |
| 132 | DFS_DLLPRG1_VSETTLCTRL_SET(0x1) | |
| 133 | DFS_DLLPRG1_CALBYPEN_SET(0x0) | |
| 134 | DFS_DLLPRG1_DACIN_SET(0x1) | DFS_DLLPRG1_LCKWT_SET(0x0) | |
| 135 | DFS_DLLPRG1_V2IGC_SET(0x5), DFS_DLLPRG1(pll)); |
| 136 | |
| 137 | for (i = 0; i < dfs_nr; i++) { |
| 138 | if (dfs[i][0]) { |
| 139 | writel(DFS_DVPORTn_MFI_SET(dfs[i][2]) | |
| 140 | DFS_DVPORTn_MFN_SET(dfs[i][1]), |
| 141 | DFS_DVPORTn(pll, i)); |
| 142 | dfs_on |= (dfs[i][0] << i); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | writel(readl(DFS_CTRL(pll)) & ~DFS_CTRL_DLL_RESET, |
| 147 | DFS_CTRL(pll)); |
| 148 | writel(readl(DFS_PORTRESET(pll)) & |
| 149 | ~DFS_PORTRESET_PORTRESET_SET(dfs_on), |
| 150 | DFS_PORTRESET(pll)); |
| 151 | while ((readl(DFS_PORTSR(pll)) & dfs_on) != dfs_on) ; |
| 152 | } |
| 153 | |
| 154 | entry_to_target_mode(MC_ME_MCTL_RUN0); |
| 155 | |
| 156 | return 0; |
| 157 | |
| 158 | } |
| 159 | |
| 160 | static void aux_source_clk_config(uintptr_t cgm_addr, u8 ac, u32 source) |
| 161 | { |
| 162 | /* select the clock source */ |
| 163 | writel(MC_CGM_ACn_SEL_SET(source), CGM_ACn_SC(cgm_addr, ac)); |
| 164 | } |
| 165 | |
| 166 | static void aux_div_clk_config(uintptr_t cgm_addr, u8 ac, u8 dc, u32 divider) |
| 167 | { |
| 168 | /* set the divider */ |
| 169 | writel(MC_CGM_ACn_DCm_DE | MC_CGM_ACn_DCm_PREDIV(divider), |
| 170 | CGM_ACn_DCm(cgm_addr, ac, dc)); |
| 171 | } |
| 172 | |
| 173 | static void setup_sys_clocks(void) |
| 174 | { |
| 175 | |
| 176 | /* set ARM PLL DFS 1 as SYSCLK */ |
| 177 | writel((readl(MC_ME_RUNn_MC(0)) & ~MC_ME_RUNMODE_MC_SYSCLK_MASK) | |
| 178 | MC_ME_RUNMODE_MC_SYSCLK(0x2), MC_ME_RUNn_MC(0)); |
| 179 | |
| 180 | entry_to_target_mode(MC_ME_MCTL_RUN0); |
| 181 | |
| 182 | /* select sysclks ARMPLL, ARMPLLDFS2, ARMPLLDFS3 */ |
| 183 | writel(MC_ME_RUNMODE_SEC_CC_I_SYSCLK |
| 184 | (0x2, |
| 185 | MC_ME_RUNMODE_SEC_CC_I_SYSCLK1_OFFSET) | |
| 186 | MC_ME_RUNMODE_SEC_CC_I_SYSCLK(0x2, |
| 187 | MC_ME_RUNMODE_SEC_CC_I_SYSCLK2_OFFSET) |
| 188 | | MC_ME_RUNMODE_SEC_CC_I_SYSCLK(0x2, |
| 189 | MC_ME_RUNMODE_SEC_CC_I_SYSCLK3_OFFSET), |
| 190 | MC_ME_RUNn_SEC_CC_I(0)); |
| 191 | |
| 192 | /* setup the sys clock divider for CORE_CLK (1000MHz) */ |
| 193 | writel(MC_CGM_SC_DCn_DE | MC_CGM_SC_DCn_PREDIV(0x0), |
| 194 | CGM_SC_DCn(MC_CGM1_BASE_ADDR, 0)); |
| 195 | |
| 196 | /* setup the sys clock divider for CORE2_CLK (500MHz) */ |
| 197 | writel(MC_CGM_SC_DCn_DE | MC_CGM_SC_DCn_PREDIV(0x1), |
| 198 | CGM_SC_DCn(MC_CGM1_BASE_ADDR, 1)); |
| 199 | /* setup the sys clock divider for SYS3_CLK (266 MHz) */ |
| 200 | writel(MC_CGM_SC_DCn_DE | MC_CGM_SC_DCn_PREDIV(0x0), |
| 201 | CGM_SC_DCn(MC_CGM0_BASE_ADDR, 0)); |
| 202 | |
| 203 | /* setup the sys clock divider for SYS6_CLK (133 Mhz) */ |
| 204 | writel(MC_CGM_SC_DCn_DE | MC_CGM_SC_DCn_PREDIV(0x1), |
| 205 | CGM_SC_DCn(MC_CGM0_BASE_ADDR, 1)); |
| 206 | |
| 207 | entry_to_target_mode(MC_ME_MCTL_RUN0); |
| 208 | |
| 209 | } |
| 210 | |
| 211 | static void setup_aux_clocks(void) |
| 212 | { |
| 213 | /* |
| 214 | * setup the aux clock divider for PERI_CLK |
| 215 | * (source: PERIPH_PLL_PHI_0/5, PERI_CLK - 80 MHz) |
| 216 | */ |
| 217 | aux_source_clk_config(MC_CGM0_BASE_ADDR, 5, MC_CGM_ACn_SEL_PERPLLDIVX); |
| 218 | aux_div_clk_config(MC_CGM0_BASE_ADDR, 5, 0, 4); |
| 219 | |
| 220 | /* setup the aux clock divider for LIN_CLK (40MHz) */ |
| 221 | aux_source_clk_config(MC_CGM0_BASE_ADDR, 3, MC_CGM_ACn_SEL_PERPLLDIVX); |
| 222 | aux_div_clk_config(MC_CGM0_BASE_ADDR, 3, 0, 1); |
| 223 | |
| 224 | /* setup the aux clock divider for ENET_TIME_CLK (50MHz) */ |
| 225 | aux_source_clk_config(MC_CGM0_BASE_ADDR, 7, MC_CGM_ACn_SEL_ENETPLL); |
| 226 | aux_div_clk_config(MC_CGM0_BASE_ADDR, 7, 1, 9); |
| 227 | |
| 228 | /* setup the aux clock divider for ENET_CLK (50MHz) */ |
| 229 | aux_source_clk_config(MC_CGM2_BASE_ADDR, 2, MC_CGM_ACn_SEL_ENETPLL); |
| 230 | aux_div_clk_config(MC_CGM2_BASE_ADDR, 2, 0, 9); |
| 231 | |
| 232 | /* setup the aux clock divider for SDHC_CLK (50 MHz). */ |
| 233 | aux_source_clk_config(MC_CGM0_BASE_ADDR, 15, MC_CGM_ACn_SEL_ENETPLL); |
| 234 | aux_div_clk_config(MC_CGM0_BASE_ADDR, 15, 0, 9); |
| 235 | |
| 236 | /* setup the aux clock divider for DDR_CLK (533MHz) and APEX_SYS_CLK (266MHz) */ |
| 237 | aux_source_clk_config(MC_CGM0_BASE_ADDR, 8, MC_CGM_ACn_SEL_DDRPLL); |
| 238 | aux_div_clk_config(MC_CGM0_BASE_ADDR, 8, 0, 0); |
| 239 | /* setup the aux clock divider for DDR4_CLK (133,25MHz) */ |
| 240 | aux_div_clk_config(MC_CGM0_BASE_ADDR, 8, 1, 3); |
| 241 | |
| 242 | entry_to_target_mode(MC_ME_MCTL_RUN0); |
| 243 | |
| 244 | } |
| 245 | |
| 246 | static void enable_modules_clock(void) |
| 247 | { |
| 248 | /* PIT0 */ |
| 249 | writeb(MC_ME_PCTLn_RUNPCm(0), MC_ME_PCTL58); |
| 250 | /* PIT1 */ |
| 251 | writeb(MC_ME_PCTLn_RUNPCm(0), MC_ME_PCTL170); |
| 252 | /* LINFLEX0 */ |
| 253 | writeb(MC_ME_PCTLn_RUNPCm(0), MC_ME_PCTL83); |
| 254 | /* LINFLEX1 */ |
| 255 | writeb(MC_ME_PCTLn_RUNPCm(0), MC_ME_PCTL188); |
| 256 | /* ENET */ |
| 257 | writeb(MC_ME_PCTLn_RUNPCm(0), MC_ME_PCTL50); |
| 258 | /* SDHC */ |
| 259 | writeb(MC_ME_PCTLn_RUNPCm(0), MC_ME_PCTL93); |
| 260 | /* IIC0 */ |
| 261 | writeb(MC_ME_PCTLn_RUNPCm(0), MC_ME_PCTL81); |
| 262 | /* IIC1 */ |
| 263 | writeb(MC_ME_PCTLn_RUNPCm(0), MC_ME_PCTL184); |
| 264 | /* IIC2 */ |
| 265 | writeb(MC_ME_PCTLn_RUNPCm(0), MC_ME_PCTL186); |
| 266 | /* MMDC0 */ |
| 267 | writeb(MC_ME_PCTLn_RUNPCm(0), MC_ME_PCTL54); |
| 268 | /* MMDC1 */ |
| 269 | writeb(MC_ME_PCTLn_RUNPCm(0), MC_ME_PCTL162); |
| 270 | |
| 271 | entry_to_target_mode(MC_ME_MCTL_RUN0); |
| 272 | } |
| 273 | |
| 274 | void clock_init(void) |
| 275 | { |
| 276 | unsigned int arm_dfs[ARM_PLL_PHI1_DFS_Nr][DFS_PARAMS_Nr] = { |
| 277 | {ARM_PLL_PHI1_DFS1_EN, ARM_PLL_PHI1_DFS1_MFN, |
| 278 | ARM_PLL_PHI1_DFS1_MFI}, |
| 279 | {ARM_PLL_PHI1_DFS2_EN, ARM_PLL_PHI1_DFS2_MFN, |
| 280 | ARM_PLL_PHI1_DFS2_MFI}, |
| 281 | {ARM_PLL_PHI1_DFS3_EN, ARM_PLL_PHI1_DFS3_MFN, |
| 282 | ARM_PLL_PHI1_DFS3_MFI} |
| 283 | }; |
| 284 | |
| 285 | unsigned int enet_dfs[ENET_PLL_PHI1_DFS_Nr][DFS_PARAMS_Nr] = { |
| 286 | {ENET_PLL_PHI1_DFS1_EN, ENET_PLL_PHI1_DFS1_MFN, |
| 287 | ENET_PLL_PHI1_DFS1_MFI}, |
| 288 | {ENET_PLL_PHI1_DFS2_EN, ENET_PLL_PHI1_DFS2_MFN, |
| 289 | ENET_PLL_PHI1_DFS2_MFI}, |
| 290 | {ENET_PLL_PHI1_DFS3_EN, ENET_PLL_PHI1_DFS3_MFN, |
| 291 | ENET_PLL_PHI1_DFS3_MFI}, |
| 292 | {ENET_PLL_PHI1_DFS4_EN, ENET_PLL_PHI1_DFS4_MFN, |
| 293 | ENET_PLL_PHI1_DFS4_MFI} |
| 294 | }; |
| 295 | |
| 296 | unsigned int ddr_dfs[DDR_PLL_PHI1_DFS_Nr][DFS_PARAMS_Nr] = { |
| 297 | {DDR_PLL_PHI1_DFS1_EN, DDR_PLL_PHI1_DFS1_MFN, |
| 298 | DDR_PLL_PHI1_DFS1_MFI}, |
| 299 | {DDR_PLL_PHI1_DFS2_EN, DDR_PLL_PHI1_DFS2_MFN, |
| 300 | DDR_PLL_PHI1_DFS2_MFI}, |
| 301 | {DDR_PLL_PHI1_DFS3_EN, DDR_PLL_PHI1_DFS3_MFN, |
| 302 | DDR_PLL_PHI1_DFS3_MFI} |
| 303 | }; |
| 304 | |
| 305 | writel(MC_ME_RUN_PCn_DRUN | MC_ME_RUN_PCn_RUN0 | MC_ME_RUN_PCn_RUN1 | |
| 306 | MC_ME_RUN_PCn_RUN2 | MC_ME_RUN_PCn_RUN3, MC_ME_RUN_PCn(0)); |
| 307 | |
| 308 | /* turn on FXOSC */ |
| 309 | writel(MC_ME_RUNMODE_MC_MVRON | MC_ME_RUNMODE_MC_XOSCON | |
| 310 | MC_ME_RUNMODE_MC_FIRCON | MC_ME_RUNMODE_MC_SYSCLK(0x1), |
| 311 | MC_ME_RUNn_MC(0)); |
| 312 | |
| 313 | entry_to_target_mode(MC_ME_MCTL_RUN0); |
| 314 | |
| 315 | program_pll(ARM_PLL, XOSC_CLK_FREQ, ARM_PLL_PHI0_FREQ, |
| 316 | ARM_PLL_PHI1_FREQ, ARM_PLL_PHI1_DFS_Nr, arm_dfs, |
| 317 | ARM_PLL_PLLDV_PREDIV, ARM_PLL_PLLDV_MFD, ARM_PLL_PLLDV_MFN); |
| 318 | |
| 319 | setup_sys_clocks(); |
| 320 | |
| 321 | program_pll(PERIPH_PLL, XOSC_CLK_FREQ, PERIPH_PLL_PHI0_FREQ, |
| 322 | PERIPH_PLL_PHI1_FREQ, PERIPH_PLL_PHI1_DFS_Nr, NULL, |
| 323 | PERIPH_PLL_PLLDV_PREDIV, PERIPH_PLL_PLLDV_MFD, |
| 324 | PERIPH_PLL_PLLDV_MFN); |
| 325 | |
| 326 | program_pll(ENET_PLL, XOSC_CLK_FREQ, ENET_PLL_PHI0_FREQ, |
| 327 | ENET_PLL_PHI1_FREQ, ENET_PLL_PHI1_DFS_Nr, enet_dfs, |
| 328 | ENET_PLL_PLLDV_PREDIV, ENET_PLL_PLLDV_MFD, |
| 329 | ENET_PLL_PLLDV_MFN); |
| 330 | |
| 331 | program_pll(DDR_PLL, XOSC_CLK_FREQ, DDR_PLL_PHI0_FREQ, |
| 332 | DDR_PLL_PHI1_FREQ, DDR_PLL_PHI1_DFS_Nr, ddr_dfs, |
| 333 | DDR_PLL_PLLDV_PREDIV, DDR_PLL_PLLDV_MFD, DDR_PLL_PLLDV_MFN); |
| 334 | |
| 335 | program_pll(VIDEO_PLL, XOSC_CLK_FREQ, VIDEO_PLL_PHI0_FREQ, |
| 336 | VIDEO_PLL_PHI1_FREQ, VIDEO_PLL_PHI1_DFS_Nr, NULL, |
| 337 | VIDEO_PLL_PLLDV_PREDIV, VIDEO_PLL_PLLDV_MFD, |
| 338 | VIDEO_PLL_PLLDV_MFN); |
| 339 | |
| 340 | setup_aux_clocks(); |
| 341 | |
| 342 | enable_modules_clock(); |
| 343 | |
| 344 | } |