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  }