github.com/usbarmory/tamago@v0.0.0-20240508072735-8612bbe1e454/soc/nxp/imx6ul/clock.go (about) 1 // NXP i.MX6UL clock control 2 // https://github.com/usbarmory/tamago 3 // 4 // Copyright (c) WithSecure Corporation 5 // https://foundry.withsecure.com 6 // 7 // Use of this source code is governed by the license 8 // that can be found in the LICENSE file. 9 10 package imx6ul 11 12 import ( 13 "errors" 14 15 "github.com/usbarmory/tamago/arm" 16 "github.com/usbarmory/tamago/bits" 17 "github.com/usbarmory/tamago/internal/reg" 18 ) 19 20 // Clock registers 21 const ( 22 CCM_CACRR = 0x020c4010 23 CACRR_ARM_PODF = 0 24 25 CCM_CBCDR = 0x020c4014 26 CBCDR_IPG_PODF = 8 27 28 CCM_CSCDR1 = 0x020c4024 29 CSCDR1_USDHC2_PODF = 16 30 CSCDR1_USDHC1_PODF = 11 31 CSCDR1_UART_CLK_SEL = 6 32 CSCDR1_UART_CLK_PODF = 0 33 34 CCM_CSCMR1 = 0x020c401c 35 CSCMR1_USDHC2_CLK_SEL = 17 36 CSCMR1_USDHC1_CLK_SEL = 16 37 CSCMR1_PERCLK_SEL = 6 38 CSCMR1_PERCLK_PODF = 0 39 40 CCM_ANALOG_PLL_ARM = 0x020c8000 41 PLL_LOCK = 31 42 PLL_BYPASS = 16 43 PLL_BYPASS_CLK_SRC = 14 44 PLL_ENABLE = 13 45 PLL_POWER = 12 46 PLL_DIV_SELECT = 0 47 48 CCM_ANALOG_PLL_USB1 = CCM_ANALOG_PLL_ARM + 0x10 49 CCM_ANALOG_PLL_USB2 = CCM_ANALOG_PLL_ARM + 0x20 50 PLL_EN_USB_CLKS = 6 51 52 CCM_ANALOG_PLL_ENET = CCM_ANALOG_PLL_ARM + 0xe0 53 PLL_ENET2_125M_EN = 20 54 PLL_ENET1_125M_EN = 13 55 PLL_ENET1_DIV_SELECT = 2 56 PLL_ENET0_DIV_SELECT = 0 57 58 CCM_ANALOG_PFD_480 = 0x020c80f0 59 CCM_ANALOG_PFD_528 = 0x020c8100 60 ANALOG_PFD3_CLKGATE = 31 61 ANALOG_PFD3_FRAC = 24 62 ANALOG_PFD2_CLKGATE = 23 63 ANALOG_PFD2_FRAC = 16 64 ANALOG_PFD1_CLKGATE = 15 65 ANALOG_PFD1_FRAC = 8 66 ANALOG_PFD0_CLKGATE = 7 67 ANALOG_PFD0_FRAC = 0 68 69 PMU_REG_CORE = 0x020c8140 70 CORE_REG2_TARG = 18 71 CORE_REG0_TARG = 0 72 73 CCM_CCGR0 = 0x020c4068 74 CCM_CCGR1 = 0x020c406c 75 CCM_CCGR2 = 0x020c4070 76 CCM_CCGR3 = 0x020c4074 77 CCM_CCGR5 = 0x020c407c 78 CCM_CCGR6 = 0x020c4080 79 80 CCGRx_CG15 = 30 81 CCGRx_CG14 = 28 82 CCGRx_CG13 = 26 83 CCGRx_CG12 = 24 84 CCGRx_CG11 = 22 85 CCGRx_CG10 = 20 86 CCGRx_CG9 = 18 87 CCGRx_CG8 = 16 88 CCGRx_CG7 = 14 89 CCGRx_CG6 = 12 90 CCGRx_CG5 = 10 91 CCGRx_CG4 = 8 92 CCGRx_CG3 = 6 93 CCGRx_CG2 = 4 94 CCGRx_CG1 = 2 95 CCGRx_CG0 = 0 96 ) 97 98 const ( 99 IOMUXC_GPR_GPR1 = 0x020e4004 100 ENET2_TX_CLK_DIR = 18 101 ENET1_TX_CLK_DIR = 17 102 ENET2_CLK_SEL = 14 103 ENET1_CLK_SEL = 13 104 ) 105 106 // Oscillator frequencies 107 const ( 108 OSC_FREQ = 24000000 109 PLL2_FREQ = 528000000 110 PLL3_FREQ = 480000000 111 ) 112 113 // Operating ARM core frequencies in MHz (care must be taken as not all P/Ns 114 // support the entire range) 115 // (p24, Table 10. Operating Ranges, IMX6ULLCEC). 116 const ( 117 FreqMax = Freq900 118 Freq900 = 900 119 Freq792 = 792 120 Freq528 = 528 121 Freq396 = 396 122 Freq198 = 198 123 FreqLow = Freq198 124 ) 125 126 // Clocks at boot time 127 // (p261, Table 8-4. Normal frequency clocks configuration, IMX6ULLRM) 128 const ( 129 IPG_FREQ = 66000000 130 AHB_FREQ = 132000000 131 ) 132 133 // ARMCoreDiv returns the ARM core divider value 134 // (p665, 18.6.5 CCM Arm Clock Root Register, IMX6ULLRM). 135 func ARMCoreDiv() (div float32) { 136 return float32(reg.Get(CCM_CACRR, CACRR_ARM_PODF, 0b111) + 1) 137 } 138 139 // ARMPLLDiv returns the ARM PLL divider value 140 // (p714, 18.7.1 Analog ARM PLL control Register, IMX6ULLRM). 141 func ARMPLLDiv() (div float32) { 142 return float32(reg.Get(CCM_ANALOG_PLL_ARM, PLL_DIV_SELECT, 0b1111111)) / 2 143 } 144 145 // ARMFreq returns the ARM core frequency. 146 func ARMFreq() (hz uint32) { 147 // (OSC_FREQ * (DIV_SELECT / 2)) / (ARM_PODF + 1) 148 return uint32((OSC_FREQ * ARMPLLDiv()) / ARMCoreDiv()) 149 } 150 151 func setOperatingPoint(uV uint32) { 152 var reg0Targ uint32 153 var reg2Targ uint32 154 155 curTarg := reg.Get(PMU_REG_CORE, CORE_REG0_TARG, 0b11111) 156 157 // p2456, 39.6.4 Digital Regulator Core Register, IMX6ULLRM 158 if uV < 725000 { 159 reg0Targ = 0b00000 160 } else if uV > 1450000 { 161 reg0Targ = 0b11111 162 } else { 163 reg0Targ = (uV - 700000) / 25000 164 } 165 166 if reg0Targ == curTarg { 167 return 168 } 169 170 // VDD_SOC_CAP Min is 1150000 (targ == 18) 171 if reg0Targ < 18 { 172 reg2Targ = 18 173 } else { 174 reg2Targ = reg0Targ 175 } 176 177 r := reg.Read(PMU_REG_CORE) 178 179 // set ARM core target voltage 180 bits.SetN(&r, CORE_REG0_TARG, 0b11111, reg0Targ) 181 // set SOC target voltage 182 bits.SetN(&r, CORE_REG2_TARG, 0b11111, reg2Targ) 183 184 reg.Write(PMU_REG_CORE, r) 185 arm.Busyloop(10000) 186 } 187 188 // SetARMFreq changes the ARM core frequency, see `Freq*` constants for 189 // supported values. This function allows overclocking as it does not verify 190 // P/N compatibility with the desired frequency. 191 func SetARMFreq(mhz uint32) (err error) { 192 var div_select uint32 193 var arm_podf uint32 194 var uV uint32 195 196 curMHz := ARMFreq() / 1000000 197 198 if mhz == curMHz { 199 return 200 } 201 202 // p24, Table 10. Operating Ranges, IMX6ULLCEC 203 switch mhz { 204 case Freq900: 205 div_select = 75 206 arm_podf = 0 207 uV = 1275000 208 case Freq792: 209 div_select = 66 210 arm_podf = 0 211 uV = 1225000 212 case Freq528: 213 div_select = 88 214 arm_podf = 1 215 uV = 1175000 216 case Freq396: 217 div_select = 66 218 arm_podf = 1 219 uV = 1025000 220 case Freq198: 221 div_select = 66 222 arm_podf = 3 223 uV = 950000 224 default: 225 return errors.New("unsupported") 226 } 227 228 if mhz > curMHz { 229 setOperatingPoint(uV) 230 } 231 232 // set bypass source to main oscillator 233 reg.SetN(CCM_ANALOG_PLL_ARM, PLL_BYPASS_CLK_SRC, 0b11, 0) 234 235 // bypass 236 reg.Set(CCM_ANALOG_PLL_ARM, PLL_BYPASS) 237 238 // set PLL divisor 239 reg.SetN(CCM_ANALOG_PLL_ARM, PLL_DIV_SELECT, 0b1111111, div_select) 240 241 // wait for lock 242 reg.Wait(CCM_ANALOG_PLL_ARM, PLL_LOCK, 1, 1) 243 244 // remove bypass 245 reg.Clear(CCM_ANALOG_PLL_ARM, PLL_BYPASS) 246 247 // set core divisor 248 reg.SetN(CCM_CACRR, CACRR_ARM_PODF, 0b111, arm_podf) 249 250 if mhz < curMHz { 251 setOperatingPoint(uV) 252 } 253 254 return 255 } 256 257 // GetPeripheralClock returns the IPG_CLK_ROOT frequency, 258 // (p629, Figure 18-2. Clock Tree - Part 1, IMX6ULLRM). 259 func GetPeripheralClock() uint32 { 260 // IPG_CLK_ROOT derived from AHB_CLK_ROOT which is 132 MHz 261 ipg_podf := reg.Get(CCM_CBCDR, CBCDR_IPG_PODF, 0b11) 262 return AHB_FREQ / (ipg_podf + 1) 263 } 264 265 // GetHighFrequencyClock returns the PERCLK_CLK_ROOT frequency, 266 // (p629, Figure 18-2. Clock Tree - Part 1, IMX6ULLRM). 267 func GetHighFrequencyClock() uint32 { 268 var freq uint32 269 270 if reg.Get(CCM_CSCMR1, CSCMR1_PERCLK_SEL, 1) == 1 { 271 freq = OSC_FREQ 272 } else { 273 freq = GetPeripheralClock() 274 } 275 276 podf := reg.Get(CCM_CSCMR1, CSCMR1_PERCLK_PODF, 0x3f) 277 278 return freq / (podf + 1) 279 } 280 281 // GetPFD returns the fractional divider and frequency in Hz of a PLL PFD 282 // (p734, 18.7.15 480MHz Clock (PLL3) Phase Fractional Divider Control Register, IMX6ULLRM) 283 // (p736, 18.7.16 480MHz Clock (PLL2) Phase Fractional Divider Control Register, IMX6ULLRM). 284 func GetPFD(pll int, pfd int) (div uint32, hz uint32) { 285 var register uint32 286 var div_pos, gate_pos int 287 var freq float64 288 289 switch pll { 290 case 2: 291 register = CCM_ANALOG_PFD_528 292 freq = PLL2_FREQ 293 case 3: 294 register = CCM_ANALOG_PFD_480 295 freq = PLL3_FREQ 296 default: 297 // Only PLL2 and PLL3 have PFD's. 298 return 299 } 300 301 switch pfd { 302 case 0: 303 gate_pos = ANALOG_PFD0_CLKGATE 304 div_pos = ANALOG_PFD0_FRAC 305 case 1: 306 gate_pos = ANALOG_PFD1_CLKGATE 307 div_pos = ANALOG_PFD1_FRAC 308 case 2: 309 gate_pos = ANALOG_PFD2_CLKGATE 310 div_pos = ANALOG_PFD2_FRAC 311 case 3: 312 gate_pos = ANALOG_PFD3_CLKGATE 313 div_pos = ANALOG_PFD3_FRAC 314 default: 315 return 316 } 317 318 if reg.Get(register, gate_pos, 1) == 1 { 319 return 320 } 321 322 // Output frequency has a static multiplicator of 18 323 // p646, 18.5.1.4 Phase Fractional Dividers (PFD) 324 div = reg.Get(register, div_pos, 0b111111) 325 hz = uint32((freq * 18) / float64(div)) 326 327 return 328 } 329 330 // SetPFD sets the fractional divider of a PPL PFD 331 // (p734, 18.7.15 480MHz Clock (PLL3) Phase Fractional Divider Control Register, IMX6ULLRM) 332 // (p736, 18.7.16 480MHz Clock (PLL2) Phase Fractional Divider Control Register, IMX6ULLRM). 333 func SetPFD(pll uint32, pfd uint32, div uint32) error { 334 var register uint32 335 var div_pos int 336 337 switch pll { 338 case 2: 339 register = CCM_ANALOG_PFD_528 340 case 3: 341 register = CCM_ANALOG_PFD_480 342 default: 343 return errors.New("invalid pll index") 344 } 345 346 // Divider can range from 12 to 35 347 // p646, 18.5.1.4 Phase Fractional Dividers (PFD), IMX6ULLRM. 348 if div < 12 || div > 35 { 349 return errors.New("invalid div value") 350 } 351 352 switch pfd { 353 case 0: 354 div_pos = ANALOG_PFD0_FRAC 355 case 1: 356 div_pos = ANALOG_PFD1_FRAC 357 case 2: 358 div_pos = ANALOG_PFD2_FRAC 359 case 3: 360 div_pos = ANALOG_PFD3_FRAC 361 default: 362 return errors.New("invalid pfd index") 363 } 364 365 reg.SetN(register, div_pos, 0b111111, div) 366 367 return nil 368 } 369 370 // GetUARTClock returns the UART_CLK_ROOT frequency, 371 // (p630, Figure 18-3. Clock Tree - Part 2, IMX6ULLRM). 372 func GetUARTClock() uint32 { 373 var freq uint32 374 375 if reg.Get(CCM_CSCDR1, CSCDR1_UART_CLK_SEL, 1) == 1 { 376 freq = OSC_FREQ 377 } else { 378 // match /6 static divider (p630, Figure 18-3. Clock Tree - Part 2, IMX6ULLRM) 379 freq = PLL3_FREQ / 6 380 } 381 382 podf := reg.Get(CCM_CSCDR1, CSCDR1_UART_CLK_PODF, 0b111111) 383 384 return freq / (podf + 1) 385 } 386 387 // GetUSDHCClock returns the USDHCx_CLK_ROOT clock by reading CSCMR1[USDHCx_CLK_SEL] 388 // and CSCDR1[USDHCx_PODF] 389 // (p629, Figure 18-2. Clock Tree - Part 1, IMX6ULLRM) 390 func GetUSDHCClock(index int) (podf uint32, clksel uint32, clock uint32) { 391 var podf_pos int 392 var clksel_pos int 393 var freq uint32 394 395 switch index { 396 case 1: 397 podf_pos = CSCDR1_USDHC1_PODF 398 clksel_pos = CSCMR1_USDHC1_CLK_SEL 399 case 2: 400 podf_pos = CSCDR1_USDHC2_PODF 401 clksel_pos = CSCMR1_USDHC2_CLK_SEL 402 default: 403 return 404 } 405 406 podf = reg.Get(CCM_CSCDR1, podf_pos, 0b111) 407 clksel = reg.Get(CCM_CSCMR1, clksel_pos, 1) 408 409 if clksel == 1 { 410 _, freq = GetPFD(2, 0) 411 } else { 412 _, freq = GetPFD(2, 2) 413 } 414 415 clock = freq / (podf + 1) 416 417 return 418 } 419 420 // SetUSDHCClock controls the USDHCx_CLK_ROOT clock by setting CSCMR1[USDHCx_CLK_SEL] 421 // and CSCDR1[USDHCx_PODF] 422 // (p629, Figure 18-2. Clock Tree - Part 1, IMX6ULLRM). 423 func SetUSDHCClock(index int, podf uint32, clksel uint32) (err error) { 424 var podf_pos int 425 var clksel_pos int 426 427 if podf < 0 || podf > 7 { 428 return errors.New("podf value out of range") 429 } 430 431 if clksel < 0 || clksel > 1 { 432 return errors.New("selector value out of range") 433 } 434 435 switch index { 436 case 1: 437 podf_pos = CSCDR1_USDHC1_PODF 438 clksel_pos = CSCMR1_USDHC1_CLK_SEL 439 case 2: 440 podf_pos = CSCDR1_USDHC2_PODF 441 clksel_pos = CSCMR1_USDHC2_CLK_SEL 442 default: 443 return errors.New("invalid interface index") 444 } 445 446 reg.SetN(CCM_CSCDR1, podf_pos, 0b111, podf) 447 reg.SetN(CCM_CSCMR1, clksel_pos, 1, clksel) 448 449 return 450 } 451 452 // EnableUSBPLL enables the USBPHY0 480MHz PLL. 453 func EnableUSBPLL(index int) (err error) { 454 var pll uint32 455 456 switch index { 457 case 1: 458 pll = CCM_ANALOG_PLL_USB1 459 case 2: 460 pll = CCM_ANALOG_PLL_USB2 461 default: 462 return errors.New("invalid interface index") 463 } 464 465 // power up PLL 466 reg.Set(pll, PLL_POWER) 467 reg.Set(pll, PLL_EN_USB_CLKS) 468 469 // wait for lock 470 reg.Wait(pll, PLL_LOCK, 1, 1) 471 472 // remove bypass 473 reg.Clear(pll, PLL_BYPASS) 474 475 // enable PLL 476 reg.Set(pll, PLL_ENABLE) 477 478 return 479 } 480 481 // EnableENETPLL enables the Ethernet MAC 50MHz PLL. 482 func EnableENETPLL(index int) (err error) { 483 var sel int 484 var dir int 485 var enable int 486 var div_select int 487 var pll uint32 = CCM_ANALOG_PLL_ENET 488 489 switch index { 490 case 1: 491 sel = ENET1_CLK_SEL 492 dir = ENET1_TX_CLK_DIR 493 enable = PLL_ENET1_125M_EN 494 div_select = PLL_ENET0_DIV_SELECT 495 case 2: 496 sel = ENET2_CLK_SEL 497 dir = ENET2_TX_CLK_DIR 498 enable = PLL_ENET2_125M_EN 499 div_select = PLL_ENET1_DIV_SELECT 500 default: 501 return errors.New("invalid interface index") 502 } 503 504 // set reference clock 505 reg.Clear(IOMUXC_GPR_GPR1, sel) 506 reg.Set(IOMUXC_GPR_GPR1, dir) 507 508 // set frequency to 50MHz 509 reg.SetN(pll, div_select, 0b11, 1) 510 511 // power up PLL 512 reg.Clear(pll, PLL_POWER) 513 514 // wait for lock 515 reg.Wait(pll, PLL_LOCK, 1, 1) 516 517 // enable PLL 518 reg.Set(pll, enable) 519 520 // remove bypass 521 reg.Clear(pll, PLL_BYPASS) 522 523 return 524 }