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