blob: f409768ab353315349566a6dbbebdeaf67fd1c05 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
//go:build rp2040 || rp2350
package machine
import (
"device/rp"
"runtime/volatile"
"unsafe"
)
type pll struct {
cs volatile.Register32
pwr volatile.Register32
fbDivInt volatile.Register32
prim volatile.Register32
}
var (
pllSys = (*pll)(unsafe.Pointer(rp.PLL_SYS))
pllUSB = (*pll)(unsafe.Pointer(rp.PLL_USB))
)
// init initializes pll (Sys or USB) given the following parameters.
//
// Input clock divider, refdiv.
//
// Requested output frequency from the VCO (voltage controlled oscillator), vcoFreq.
//
// Post Divider 1, postDiv1 with range 1-7 and be >= postDiv2.
//
// Post Divider 2, postDiv2 with range 1-7.
func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
refFreq := xoscFreq / refdiv
// What are we multiplying the reference clock by to get the vco freq
// (The regs are called div, because you divide the vco output and compare it to the refclk)
fbdiv := vcoFreq / (refFreq * MHz)
// Check fbdiv range
if !(fbdiv >= 16 && fbdiv <= 320) {
panic("fbdiv should be in the range [16,320]")
}
// Check divider ranges
if !((postDiv1 >= 1 && postDiv1 <= 7) && (postDiv2 >= 1 && postDiv2 <= 7)) {
panic("postdiv1, postdiv1 should be in the range [1,7]")
}
// postDiv1 should be >= postDiv2
// from appnote page 11
// postdiv1 is designed to operate with a higher input frequency
// than postdiv2
if postDiv1 < postDiv2 {
panic("postdiv1 should be greater than or equal to postdiv2")
}
// Check that reference frequency is no greater than vco / 16
if refFreq > vcoFreq/16 {
panic("reference frequency should not be greater than vco frequency divided by 16")
}
// div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10
pdiv := postDiv1<<rp.PLL_SYS_PRIM_POSTDIV1_Pos | postDiv2<<rp.PLL_SYS_PRIM_POSTDIV2_Pos
if pll.cs.HasBits(rp.PLL_SYS_CS_LOCK) &&
refdiv == pll.cs.Get()&rp.PLL_SYS_CS_REFDIV_Msk &&
fbdiv == pll.fbDivInt.Get()&rp.PLL_SYS_FBDIV_INT_FBDIV_INT_Msk &&
pdiv == pll.prim.Get()&(rp.PLL_SYS_PRIM_POSTDIV1_Msk&rp.PLL_SYS_PRIM_POSTDIV2_Msk) {
// do not disrupt PLL that is already correctly configured and operating
return
}
var pllRst uint32
if pll == pllSys {
pllRst = rp.RESETS_RESET_PLL_SYS
} else {
pllRst = rp.RESETS_RESET_PLL_USB
}
resetBlock(pllRst)
unresetBlockWait(pllRst)
// Load VCO-related dividers before starting VCO
pll.cs.Set(refdiv)
pll.fbDivInt.Set(fbdiv)
// Turn on PLL
pwr := uint32(rp.PLL_SYS_PWR_PD | rp.PLL_SYS_PWR_VCOPD)
pll.pwr.ClearBits(pwr)
// Wait for PLL to lock
for !(pll.cs.HasBits(rp.PLL_SYS_CS_LOCK)) {
}
// Set up post dividers
pll.prim.Set(pdiv)
// Turn on post divider
pll.pwr.ClearBits(rp.PLL_SYS_PWR_POSTDIVPD)
}
|