1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include "pico/runtime_init.h"
8 #if !PICO_RUNTIME_NO_INIT_CLOCKS
9 
10 #include "hardware/clocks.h"
11 #include "hardware/pll.h"
12 #include "hardware/ticks.h"
13 #include "hardware/xosc.h"
14 #if PICO_RP2040
15 #include "hardware/regs/rtc.h"
16 #endif
17 
18 #if PICO_RP2040
19 // The RTC clock frequency is 48MHz divided by power of 2 (to ensure an integer
20 // division ratio will be used in the clocks block).  A divisor of 1024 generates
21 // an RTC clock tick of 46875Hz.  This frequency is relatively close to the
22 // customary 32 or 32.768kHz 'slow clock' crystals and provides good timing resolution.
23 #define RTC_CLOCK_FREQ_HZ       (USB_CLK_HZ / 1024)
24 #endif
25 
start_all_ticks(void)26 static void start_all_ticks(void) {
27     uint32_t cycles = clock_get_hz(clk_ref) / PICO_MHZ;
28     // Note RP2040 has a single tick generator in the watchdog which serves
29     // watchdog, system timer and M0+ SysTick; The tick generator is clocked from clk_ref
30     // but is now adapted by the hardware_ticks library for compatibility with RP2350
31     // npte: hardware_ticks library now provides an adapter for RP2040
32 
33     for (int i = 0; i < (int)TICK_COUNT; ++i) {
34         tick_start((tick_gen_num_t)i, cycles);
35     }
36 }
37 
runtime_init_clocks(void)38 void __weak runtime_init_clocks(void) {
39     // Note: These need setting *before* the ticks are started
40     if (running_on_fpga()) {
41         for (uint i = 0; i < CLK_COUNT; i++) {
42             clock_set_reported_hz(i, 48 * PICO_MHZ);
43         }
44         // clk_ref is 12MHz in both RP2040 and RP2350 FPGA
45         clock_set_reported_hz(clk_ref, 12 * PICO_MHZ);
46         // RP2040 has an extra clock, the rtc
47 #if HAS_RP2040_RTC
48         clock_set_reported_hz(clk_rtc, RTC_CLOCK_FREQ_HZ);
49 #endif
50     } else {
51         // Disable resus that may be enabled from previous software
52         clocks_hw->resus.ctrl = 0;
53 
54         // Enable the xosc
55         xosc_init();
56 
57         // Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
58         hw_clear_bits(&clocks_hw->clk[clk_sys].ctrl, CLOCKS_CLK_SYS_CTRL_SRC_BITS);
59         while (clocks_hw->clk[clk_sys].selected != 0x1)
60             tight_loop_contents();
61         hw_clear_bits(&clocks_hw->clk[clk_ref].ctrl, CLOCKS_CLK_REF_CTRL_SRC_BITS);
62         while (clocks_hw->clk[clk_ref].selected != 0x1)
63             tight_loop_contents();
64 
65         /// \tag::pll_init[]
66         pll_init(pll_sys, PLL_SYS_REFDIV, PLL_SYS_VCO_FREQ_HZ, PLL_SYS_POSTDIV1, PLL_SYS_POSTDIV2);
67         pll_init(pll_usb, PLL_USB_REFDIV, PLL_USB_VCO_FREQ_HZ, PLL_USB_POSTDIV1, PLL_USB_POSTDIV2);
68         /// \end::pll_init[]
69 
70         // Configure clocks
71 
72         // RP2040 CLK_REF = XOSC (usually) 12MHz / 1 = 12MHz
73         // RP2350 CLK_REF = XOSC (XOSC_MHZ) / N (1,2,4) = 12MHz
74 
75         // clk_ref aux select is 0 because:
76         //
77         // - RP2040: no aux mux on clk_ref, so this field is don't-care.
78         //
79         // - RP2350: there is an aux mux, but we are selecting one of the
80         //   non-aux inputs to the glitchless mux, so the aux select doesn't
81         //   matter. The value of 0 here happens to be the sys PLL.
82 
83         clock_configure_undivided(clk_ref,
84                         CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC,
85                         0,
86                         XOSC_HZ);
87 
88         /// \tag::configure_clk_sys[]
89         // CLK SYS = PLL SYS (usually) 125MHz / 1 = 125MHz
90         clock_configure_undivided(clk_sys,
91                         CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
92                         CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
93                         SYS_CLK_HZ);
94         /// \end::configure_clk_sys[]
95 
96         // CLK USB = PLL USB 48MHz / 1 = 48MHz
97         clock_configure_undivided(clk_usb,
98                         0, // No GLMUX
99                         CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
100                         USB_CLK_HZ);
101 
102         // CLK ADC = PLL USB 48MHZ / 1 = 48MHz
103         clock_configure_undivided(clk_adc,
104                         0, // No GLMUX
105                         CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
106                         USB_CLK_HZ);
107 
108 #if HAS_RP2040_RTC
109         // CLK RTC = PLL USB 48MHz / 1024 = 46875Hz
110 #if (USB_CLK_HZ % RTC_CLOCK_FREQ_HZ == 0)
111         // this doesn't pull in 64 bit arithmetic
112         clock_configure_int_divider(clk_rtc,
113                         0, // No GLMUX
114                         CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
115                         USB_CLK_HZ,
116                         USB_CLK_HZ / RTC_CLOCK_FREQ_HZ);
117 
118 #else
119         clock_configure(clk_rtc,
120                         0, // No GLMUX
121                         CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
122                         USB_CLK_HZ,
123                         RTC_CLOCK_FREQ_HZ);
124 
125 #endif
126 #endif
127 
128         // CLK PERI = clk_sys. Used as reference clock for UART and SPI serial.
129         clock_configure_undivided(clk_peri,
130                         0,
131                         CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
132                         SYS_CLK_HZ);
133 
134 #if HAS_HSTX
135         // CLK_HSTX = clk_sys. Transmit bit clock for the HSTX peripheral.
136         clock_configure_undivided(clk_hstx,
137                         0,
138                         CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLK_SYS,
139                         SYS_CLK_HZ);
140 #endif
141     }
142 
143     // Finally, all clocks are configured so start the ticks
144     // The ticks use clk_ref so now that is configured we can start them
145     start_all_ticks();
146 }
147 
148 #endif