77// For MHZ definitions etc
88#include "hardware/clocks.h"
99#include "hardware/pll.h"
10+ #include "hardware/resets.h"
1011
1112/// \tag::pll_init_calculations[]
1213void pll_init (PLL pll , uint refdiv , uint vco_freq , uint post_div1 , uint post_div2 ) {
13- // Turn off PLL in case it is already running
14- pll -> pwr = 0xffffffff ;
15- pll -> fbdiv_int = 0 ;
16-
1714 uint32_t ref_mhz = XOSC_MHZ / refdiv ;
18- pll -> cs = refdiv ;
1915
2016 // What are we multiplying the reference clock by to get the vco freq
2117 // (The regs are called div, because you divide the vco output and compare it to the refclk)
@@ -34,11 +30,28 @@ void pll_init(PLL pll, uint refdiv, uint vco_freq, uint post_div1, uint post_div
3430 // than postdiv2
3531 assert (post_div2 <= post_div1 );
3632
37- /// \tag::pll_init_finish[]
3833 // Check that reference frequency is no greater than vco / 16
3934 assert (ref_mhz <= (vco_freq / 16 ));
4035
41- // Put calculated value into feedback divider
36+ // div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10
37+ uint32_t pdiv = (post_div1 << PLL_PRIM_POSTDIV1_LSB ) |
38+ (post_div2 << PLL_PRIM_POSTDIV2_LSB );
39+
40+ /// \tag::pll_init_finish[]
41+ if ((pll -> cs & PLL_CS_LOCK_BITS ) &&
42+ (refdiv == (pll -> cs & PLL_CS_REFDIV_BITS )) &&
43+ (fbdiv == (pll -> fbdiv_int & PLL_FBDIV_INT_BITS )) &&
44+ (pdiv == (pll -> prim & (PLL_PRIM_POSTDIV1_BITS & PLL_PRIM_POSTDIV2_BITS )))) {
45+ // do not disrupt PLL that is already correctly configured and operating
46+ return ;
47+ }
48+
49+ uint32_t pll_reset = (pll_usb_hw == pll ) ? RESETS_RESET_PLL_USB_BITS : RESETS_RESET_PLL_SYS_BITS ;
50+ reset_block (pll_reset );
51+ unreset_block_wait (pll_reset );
52+
53+ // Load VCO-related dividers before starting VCO
54+ pll -> cs = refdiv ;
4255 pll -> fbdiv_int = fbdiv ;
4356
4457 // Turn on PLL
@@ -50,9 +63,7 @@ void pll_init(PLL pll, uint refdiv, uint vco_freq, uint post_div1, uint post_div
5063 // Wait for PLL to lock
5164 while (!(pll -> cs & PLL_CS_LOCK_BITS )) tight_loop_contents ();
5265
53- // Set up post dividers - div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10
54- uint32_t pdiv = (post_div1 << PLL_PRIM_POSTDIV1_LSB ) |
55- (post_div2 << PLL_PRIM_POSTDIV2_LSB );
66+ // Set up post dividers
5667 pll -> prim = pdiv ;
5768
5869 // Turn on post divider
@@ -63,4 +74,4 @@ void pll_init(PLL pll, uint refdiv, uint vco_freq, uint post_div1, uint post_div
6374void pll_deinit (PLL pll ) {
6475 // todo: Make sure there are no sources running from this pll?
6576 pll -> pwr = PLL_PWR_BITS ;
66- }
77+ }
0 commit comments