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  }