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