graham sanderson | 26653ea | 2021-01-20 10:44:27 -0600 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | */ |
| 6 | |
andygpz11 | a42564b | 2023-03-21 17:49:20 +0000 | [diff] [blame] | 7 | // For frequency and PLL definitions etc. |
graham sanderson | 26653ea | 2021-01-20 10:44:27 -0600 | [diff] [blame] | 8 | #include "hardware/clocks.h" |
| 9 | #include "hardware/pll.h" |
majbthrd | 1620986 | 2021-04-06 04:42:18 -0500 | [diff] [blame] | 10 | #include "hardware/resets.h" |
graham sanderson | 26653ea | 2021-01-20 10:44:27 -0600 | [diff] [blame] | 11 | |
| 12 | /// \tag::pll_init_calculations[] |
graham sanderson | 503bc8b | 2021-02-19 12:05:13 -0600 | [diff] [blame] | 13 | void pll_init(PLL pll, uint refdiv, uint vco_freq, uint post_div1, uint post_div2) { |
andygpz11 | a42564b | 2023-03-21 17:49:20 +0000 | [diff] [blame] | 14 | uint32_t ref_freq = XOSC_KHZ * KHZ / refdiv; |
graham sanderson | 26653ea | 2021-01-20 10:44:27 -0600 | [diff] [blame] | 15 | |
Liam Fraser | 33818dd | 2022-06-20 16:28:03 +0100 | [diff] [blame] | 16 | // Check vco freq is in an acceptable range |
andygpz11 | a42564b | 2023-03-21 17:49:20 +0000 | [diff] [blame] | 17 | assert(vco_freq >= (PICO_PLL_VCO_MIN_FREQ_KHZ * KHZ) && vco_freq <= (PICO_PLL_VCO_MAX_FREQ_KHZ * KHZ)); |
Liam Fraser | 33818dd | 2022-06-20 16:28:03 +0100 | [diff] [blame] | 18 | |
graham sanderson | 26653ea | 2021-01-20 10:44:27 -0600 | [diff] [blame] | 19 | // What are we multiplying the reference clock by to get the vco freq |
| 20 | // (The regs are called div, because you divide the vco output and compare it to the refclk) |
andygpz11 | a42564b | 2023-03-21 17:49:20 +0000 | [diff] [blame] | 21 | uint32_t fbdiv = vco_freq / ref_freq; |
graham sanderson | 26653ea | 2021-01-20 10:44:27 -0600 | [diff] [blame] | 22 | /// \end::pll_init_calculations[] |
| 23 | |
| 24 | // fbdiv |
| 25 | assert(fbdiv >= 16 && fbdiv <= 320); |
| 26 | |
| 27 | // Check divider ranges |
| 28 | assert((post_div1 >= 1 && post_div1 <= 7) && (post_div2 >= 1 && post_div2 <= 7)); |
| 29 | |
| 30 | // post_div1 should be >= post_div2 |
| 31 | // from appnote page 11 |
Liam Fraser | c578422 | 2023-01-24 15:10:05 +0000 | [diff] [blame] | 32 | // postdiv1 is designed to operate with a higher input frequency than postdiv2 |
graham sanderson | 26653ea | 2021-01-20 10:44:27 -0600 | [diff] [blame] | 33 | |
graham sanderson | 26653ea | 2021-01-20 10:44:27 -0600 | [diff] [blame] | 34 | // Check that reference frequency is no greater than vco / 16 |
andygpz11 | a42564b | 2023-03-21 17:49:20 +0000 | [diff] [blame] | 35 | assert(ref_freq <= (vco_freq / 16)); |
graham sanderson | 26653ea | 2021-01-20 10:44:27 -0600 | [diff] [blame] | 36 | |
majbthrd | 1620986 | 2021-04-06 04:42:18 -0500 | [diff] [blame] | 37 | // div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10 |
| 38 | uint32_t pdiv = (post_div1 << PLL_PRIM_POSTDIV1_LSB) | |
| 39 | (post_div2 << PLL_PRIM_POSTDIV2_LSB); |
| 40 | |
| 41 | /// \tag::pll_init_finish[] |
| 42 | if ((pll->cs & PLL_CS_LOCK_BITS) && |
| 43 | (refdiv == (pll->cs & PLL_CS_REFDIV_BITS)) && |
| 44 | (fbdiv == (pll->fbdiv_int & PLL_FBDIV_INT_BITS)) && |
Graham Sanderson | e1c5fd3 | 2022-05-09 14:52:38 -0500 | [diff] [blame] | 45 | (pdiv == (pll->prim & (PLL_PRIM_POSTDIV1_BITS | PLL_PRIM_POSTDIV2_BITS)))) { |
majbthrd | 1620986 | 2021-04-06 04:42:18 -0500 | [diff] [blame] | 46 | // do not disrupt PLL that is already correctly configured and operating |
| 47 | return; |
| 48 | } |
| 49 | |
| 50 | uint32_t pll_reset = (pll_usb_hw == pll) ? RESETS_RESET_PLL_USB_BITS : RESETS_RESET_PLL_SYS_BITS; |
| 51 | reset_block(pll_reset); |
| 52 | unreset_block_wait(pll_reset); |
| 53 | |
| 54 | // Load VCO-related dividers before starting VCO |
| 55 | pll->cs = refdiv; |
graham sanderson | 26653ea | 2021-01-20 10:44:27 -0600 | [diff] [blame] | 56 | pll->fbdiv_int = fbdiv; |
| 57 | |
| 58 | // Turn on PLL |
| 59 | uint32_t power = PLL_PWR_PD_BITS | // Main power |
| 60 | PLL_PWR_VCOPD_BITS; // VCO Power |
| 61 | |
| 62 | hw_clear_bits(&pll->pwr, power); |
| 63 | |
| 64 | // Wait for PLL to lock |
| 65 | while (!(pll->cs & PLL_CS_LOCK_BITS)) tight_loop_contents(); |
| 66 | |
majbthrd | 1620986 | 2021-04-06 04:42:18 -0500 | [diff] [blame] | 67 | // Set up post dividers |
graham sanderson | 26653ea | 2021-01-20 10:44:27 -0600 | [diff] [blame] | 68 | pll->prim = pdiv; |
| 69 | |
| 70 | // Turn on post divider |
| 71 | hw_clear_bits(&pll->pwr, PLL_PWR_POSTDIVPD_BITS); |
| 72 | /// \end::pll_init_finish[] |
| 73 | } |
| 74 | |
| 75 | void pll_deinit(PLL pll) { |
| 76 | // todo: Make sure there are no sources running from this pll? |
| 77 | pll->pwr = PLL_PWR_BITS; |
majbthrd | 1620986 | 2021-04-06 04:42:18 -0500 | [diff] [blame] | 78 | } |