github.com/f-secure-foundry/tamago@v0.0.0-20220307101044-d73fcdd7f11b/soc/imx6/clock.go (about) 1 // NXP i.MX6UL ARM clock control 2 // https://github.com/f-secure-foundry/tamago 3 // 4 // Copyright (c) F-Secure Corporation 5 // https://foundry.f-secure.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 imx6 11 12 import ( 13 "errors" 14 15 "github.com/f-secure-foundry/tamago/arm" 16 "github.com/f-secure-foundry/tamago/bits" 17 "github.com/f-secure-foundry/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_CLK_PODF = 16 30 CSCDR1_USDHC1_CLK_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_PFD_480 = 0x020c80f0 53 CCM_ANALOG_PFD_528 = 0x020c8100 54 ANALOG_PFD3_CLKGATE = 31 55 ANALOG_PFD3_FRAC = 24 56 ANALOG_PFD2_CLKGATE = 23 57 ANALOG_PFD2_FRAC = 16 58 ANALOG_PFD1_CLKGATE = 15 59 ANALOG_PFD1_FRAC = 8 60 ANALOG_PFD0_CLKGATE = 7 61 ANALOG_PFD0_FRAC = 0 62 63 PMU_REG_CORE = 0x020c8140 64 CORE_REG2_TARG = 18 65 CORE_REG0_TARG = 0 66 67 CCM_CCGR1 = 0x020c406c 68 CCM_CCGR2 = 0x020c4070 69 CCM_CCGR6 = 0x020c4080 70 71 CCGRx_CG15 = 30 72 CCGRx_CG14 = 28 73 CCGRx_CG13 = 26 74 CCGRx_CG12 = 24 75 CCGRx_CG11 = 22 76 CCGRx_CG10 = 20 77 CCGRx_CG9 = 18 78 CCGRx_CG8 = 16 79 CCGRx_CG7 = 14 80 CCGRx_CG6 = 12 81 CCGRx_CG5 = 10 82 CCGRx_CG4 = 8 83 CCGRx_CG3 = 6 84 CCGRx_CG2 = 4 85 CCGRx_CG1 = 2 86 CCGRx_CG0 = 0 87 ) 88 89 // Oscillator frequencies 90 const ( 91 OSC_FREQ = 24000000 92 PLL2_FREQ = 528000000 93 PLL3_FREQ = 480000000 94 ) 95 96 // Operating ARM core frequencies in MHz 97 // (p24, Table 10. Operating Ranges, IMX6ULLCEC). 98 const ( 99 FreqMax = Freq900 100 Freq900 = 900 101 Freq792 = 792 102 Freq528 = 528 103 Freq396 = 396 104 Freq198 = 198 105 FreqLow = Freq198 106 ) 107 108 // ARMCoreDiv returns the ARM core divider value 109 // (p665, 18.6.5 CCM Arm Clock Root Register, IMX6ULLRM). 110 func ARMCoreDiv() (div float32) { 111 return float32(reg.Get(CCM_CACRR, CACRR_ARM_PODF, 0b111) + 1) 112 } 113 114 // ARMPLLDiv returns the ARM PLL divider value 115 // (p714, 18.7.1 Analog ARM PLL control Register, IMX6ULLRM). 116 func ARMPLLDiv() (div float32) { 117 return float32(reg.Get(CCM_ANALOG_PLL_ARM, PLL_DIV_SELECT, 0b1111111)) / 2 118 } 119 120 // ARMFreq returns the ARM core frequency. 121 func ARMFreq() (hz uint32) { 122 // (OSC_FREQ * (DIV_SELECT / 2)) / (ARM_PODF + 1) 123 return uint32((OSC_FREQ * ARMPLLDiv()) / ARMCoreDiv()) 124 } 125 126 func setOperatingPointIMX6ULL(uV uint32) { 127 var reg0Targ uint32 128 var reg2Targ uint32 129 130 curTarg := reg.Get(PMU_REG_CORE, CORE_REG0_TARG, 0b11111) 131 132 // p2456, 39.6.4 Digital Regulator Core Register, IMX6ULLRM 133 if uV < 725000 { 134 reg0Targ = 0b00000 135 } else if uV > 1450000 { 136 reg0Targ = 0b11111 137 } else { 138 reg0Targ = (uV - 700000) / 25000 139 } 140 141 if reg0Targ == curTarg { 142 return 143 } 144 145 // VDD_SOC_CAP Min is 1150000 (targ == 18) 146 if reg0Targ < 18 { 147 reg2Targ = 18 148 } else { 149 reg2Targ = reg0Targ 150 } 151 152 r := reg.Read(PMU_REG_CORE) 153 154 // set ARM core target voltage 155 bits.SetN(&r, CORE_REG0_TARG, 0b11111, reg0Targ) 156 // set SOC target voltage 157 bits.SetN(&r, CORE_REG2_TARG, 0b11111, reg2Targ) 158 159 reg.Write(PMU_REG_CORE, r) 160 arm.Busyloop(10000) 161 } 162 163 func setARMFreqIMX6ULL(mhz uint32) (err error) { 164 var div_select uint32 165 var arm_podf uint32 166 var uV uint32 167 168 curMHz := ARMFreq() / 1000000 169 170 if mhz == curMHz { 171 return 172 } 173 174 // p24, Table 10. Operating Ranges, IMX6ULLCEC 175 switch mhz { 176 case Freq900: 177 div_select = 75 178 arm_podf = 0 179 uV = 1275000 180 case Freq792: 181 div_select = 66 182 arm_podf = 0 183 uV = 1225000 184 case Freq528: 185 div_select = 88 186 arm_podf = 1 187 uV = 1175000 188 case Freq396: 189 div_select = 66 190 arm_podf = 1 191 uV = 1025000 192 case Freq198: 193 div_select = 66 194 arm_podf = 3 195 uV = 950000 196 default: 197 return errors.New("unsupported") 198 } 199 200 if mhz > curMHz { 201 setOperatingPointIMX6ULL(uV) 202 } 203 204 // set bypass source to main oscillator 205 reg.SetN(CCM_ANALOG_PLL_ARM, PLL_BYPASS_CLK_SRC, 0b11, 0) 206 207 // bypass 208 reg.Set(CCM_ANALOG_PLL_ARM, PLL_BYPASS) 209 210 // set PLL divisor 211 reg.SetN(CCM_ANALOG_PLL_ARM, PLL_DIV_SELECT, 0b1111111, div_select) 212 213 // wait for lock 214 reg.Wait(CCM_ANALOG_PLL_ARM, PLL_LOCK, 1, 1) 215 216 // remove bypass 217 reg.Clear(CCM_ANALOG_PLL_ARM, PLL_BYPASS) 218 219 // set core divisor 220 reg.SetN(CCM_CACRR, CACRR_ARM_PODF, 0b111, arm_podf) 221 222 if mhz < curMHz { 223 setOperatingPointIMX6ULL(uV) 224 } 225 226 return 227 } 228 229 // SetARMFreq changes the ARM core frequency, see `Freq*` constants for 230 // supported values. 231 func SetARMFreq(mhz uint32) (err error) { 232 switch Family { 233 case IMX6ULL: 234 err = setARMFreqIMX6ULL(mhz) 235 default: 236 err = errors.New("unsupported") 237 } 238 239 return 240 } 241 242 // GetPFD returns the fractional divider and frequency in Hz of a PLL PFD 243 // (p734, 18.7.15 480MHz Clock (PLL3) Phase Fractional Divider Control Register, IMX6ULLRM) 244 // (p736, 18.7.16 480MHz Clock (PLL2) Phase Fractional Divider Control Register, IMX6ULLRM). 245 func GetPFD(pll int, pfd int) (div uint32, hz uint32) { 246 var register uint32 247 var div_pos, gate_pos int 248 var freq float64 249 250 switch pll { 251 case 2: 252 register = CCM_ANALOG_PFD_528 253 freq = PLL2_FREQ 254 case 3: 255 register = CCM_ANALOG_PFD_480 256 freq = PLL3_FREQ 257 default: 258 // Only PLL2 and PLL3 have PFD's. 259 return 260 } 261 262 switch pfd { 263 case 0: 264 gate_pos = ANALOG_PFD0_CLKGATE 265 div_pos = ANALOG_PFD0_FRAC 266 case 1: 267 gate_pos = ANALOG_PFD1_CLKGATE 268 div_pos = ANALOG_PFD1_FRAC 269 case 2: 270 gate_pos = ANALOG_PFD2_CLKGATE 271 div_pos = ANALOG_PFD2_FRAC 272 case 3: 273 gate_pos = ANALOG_PFD3_CLKGATE 274 div_pos = ANALOG_PFD3_FRAC 275 default: 276 return 277 } 278 279 if reg.Get(register, gate_pos, 0b1) == 1 { 280 return 281 } 282 283 // Output frequency has a static multiplicator of 18 284 // p646, 18.5.1.4 Phase Fractional Dividers (PFD) 285 div = reg.Get(register, div_pos, 0b111111) 286 hz = uint32((freq * 18) / float64(div)) 287 288 return 289 } 290 291 // SetPFD sets the fractional divider of a PPL PFD 292 // (p734, 18.7.15 480MHz Clock (PLL3) Phase Fractional Divider Control Register, IMX6ULLRM) 293 // (p736, 18.7.16 480MHz Clock (PLL2) Phase Fractional Divider Control Register, IMX6ULLRM). 294 func SetPFD(pll uint32, pfd uint32, div uint32) error { 295 var register uint32 296 var div_pos int 297 298 switch pll { 299 case 2: 300 register = CCM_ANALOG_PFD_528 301 case 3: 302 register = CCM_ANALOG_PFD_480 303 default: 304 return errors.New("invalid pll index") 305 } 306 307 // Divider can range from 12 to 35 308 // p646, 18.5.1.4 Phase Fractional Dividers (PFD), IMX6ULLRM. 309 if div < 12 || div > 35 { 310 return errors.New("invalid div value") 311 } 312 313 switch pfd { 314 case 0: 315 div_pos = ANALOG_PFD0_FRAC 316 case 1: 317 div_pos = ANALOG_PFD1_FRAC 318 case 2: 319 div_pos = ANALOG_PFD2_FRAC 320 case 3: 321 div_pos = ANALOG_PFD3_FRAC 322 default: 323 return errors.New("invalid pfd index") 324 } 325 326 reg.SetN(register, div_pos, 0b111111, div) 327 328 return nil 329 }