1 /* SPDX-License-Identifier: GPL-2.0-only */
3 // This file is used for setting up clocks and get devices out of reset
4 // For more Information see FU740-C000 Manual Chapter 7 Clocking and Reset
7 #include <device/mmio.h>
8 #include <soc/addressmap.h>
13 // Clock frequencies for the cores, ddr and the peripherals are all derived from the hfclk (high frequency clock) and it is always 26 MHz
14 #define FU740_HFCLK_FREQ (26 * MHz)
17 u32 hfxosccfg
; // offset 0x00
18 u32 core_pllcfg
; // offset 0x04
19 u32 core_plloutdiv
; // offset 0x08
20 u32 ddr_pllcfg
; // offset 0x0c
21 u32 ddr_plloutdiv
; // offset 0x10
22 u32 pcieaux_plloutdiv
; // offset 0x14 (undocumented)
23 u32 reserved18
; // offset 0x18
24 u32 gemgxl_pllcfg
; // offset 0x1c
25 u32 gemgxl_plloutdiv
; // offset 0x20
26 u32 core_clk_sel_reg
; // offset 0x24
27 u32 devices_reset_n
; // offset 0x28
28 u32 clk_mux_status
; // offset 0x2C
29 u32 cltx_pllcfg
; // offset 0x30 chiplink (undocumented)
30 u32 cltx_plloutdiv
; // offset 0x34 chiplink (undocumented)
31 u32 dvfs_core_pllcfg
; // offset 0x38
32 u32 dvfs_core_plloutdiv
; // offset 0x3C
33 u32 corepllsel
; // offset 0x40 (undocumented, but probably same as last gen)
34 u8 reserved44
[12]; // offset 0x44
35 u32 hfpclk_pllcfg
; // offset 0x50
36 u32 hfpclk_plloutdiv
; // offset 0x54
37 u32 hfpclkpllsel
; // offset 0x58 (undocumented, but probably same as last gen)
38 u32 hfpclk_div_reg
; // offset 0x5C
39 u8 reserved60
[128]; // offset 0x60
40 u32 prci_plls
; // offset 0xE0
41 u8 reservedE4
[12]; // offset 0xE4
42 u32 procmoncfg_core_clock
; // offset 0xF0 (undocumented)
45 static struct prci_ctlr
*prci
= (void *)FU740_PRCI
;
47 // =================================
49 // =================================
51 #define PRCI_COREPLLSEL_MASK 1
52 #define PRCI_COREPLLSEL_COREPLL 0
53 #define PRCI_COREPLLSEL_DVFSCOREPLL 1
55 #define PRCI_CORECLKSEL_MASK 1
56 #define PRCI_CORECLKSEL_CORECLKPLL 0
57 #define PRCI_CORECLKSEL_HFCLK 1
59 #define PRCI_HFPCLKSEL_MASK 1
60 #define PRCI_HFPCLKSEL_PLL 0
61 #define PRCI_HFPCLKSEL_HFCLK 1
63 // ===================================
64 // pllcfg register format is used by all PLLs
65 // ===================================
67 #define PRCI_PLLCFG_DIVR_SHIFT 0
68 #define PRCI_PLLCFG_DIVF_SHIFT 6
69 #define PRCI_PLLCFG_DIVQ_SHIFT 15
70 #define PRCI_PLLCFG_RANGE_SHIFT 18
71 #define PRCI_PLLCFG_BYPASS_SHIFT 24
72 #define PRCI_PLLCFG_FSEBYPASS_SHIFT 25
73 #define PRCI_PLLCFG_LOCK_SHIFT 31
75 #define PRCI_PLLCFG_DIVR_MASK (0x03f << PRCI_PLLCFG_DIVR_SHIFT)
76 #define PRCI_PLLCFG_DIVF_MASK (0x1ff << PRCI_PLLCFG_DIVF_SHIFT)
77 #define PRCI_PLLCFG_DIVQ_MASK (0x007 << PRCI_PLLCFG_DIVQ_SHIFT)
78 #define PRCI_PLLCFG_RANGE_MASK (0x007 << PRCI_PLLCFG_RANGE_SHIFT)
79 #define PRCI_PLLCFG_BYPASS_MASK (0x001 << PRCI_PLLCFG_BYPASS_SHIFT)
80 #define PRCI_PLLCFG_FSEBYPASS_MASK (0x001 << PRCI_PLLCFG_FSEBYPASS_SHIFT)
81 #define PRCI_PLLCFG_LOCK_MASK (0x001 << PRCI_PLLCFG_LOCK_SHIFT)
83 // ===================================
84 // plloutdiv register formats
85 // ===================================
87 // registered are used to enable/disable PLLs
88 #define PRCI_DVFSCORE_PLLOUTDIV_MASK (1 << 24) // Note: u-boot and fu740 manual differ here ...
89 #define PRCI_HFPCLK_PLLOUTDIV_MASK (1 << 31) // Note: according to u-boot it is (1 << 24) but if I use that it gets stuck
90 #define PRCI_DDR_PLLOUTDIV_MASK (1 << 31)
91 #define PRCI_GEMGXL_PLLOUTDIV_MASK (1 << 31)
92 #define PRCI_CLTX_PLLOUTDIV_MASK (1 << 24) // undocumented (chiplink tx)
93 #define PRCI_PCIEAUX_PLLOUTDIV_MASK (1 << 0) // undocumented
94 #define PRCI_CORE_PLLOUTDIV_MASK (1 << 31) // undocumented
96 // ===================================
97 // devicereset register formats
98 // ===================================
100 // used to get devices in or out of reset
101 #define PRCI_DEVICES_RESET_DDR_CTRL_RST (1 << 0) // DDR Controller
102 #define PRCI_DEVICES_RESET_DDR_AXI_RST (1 << 1) // DDR Controller AXI Interface
103 #define PRCI_DEVICES_RESET_DDR_AHB_RST (1 << 2) // DDR Controller AHB Interface
104 #define PRCI_DEVICES_RESET_DDR_PHY_RST (1 << 3) // DDR PHY
105 #define PRCI_DEVICES_RESET_PCIEAUX_RST (1 << 4)
106 #define PRCI_DEVICES_RESET_GEMGXL_RST (1 << 5) // Gigabit Ethernet Subsystem
107 #define PRCI_DEVICES_RESET_CLTX_RST (1 << 6) // chiplink reset (undocumented)
109 // ===================================
110 // prci_plls register format
111 // ===================================
113 // used to check if certain PLLs are present in the SOC
114 #define PRCI_PLLS_CLTXPLL (1 << 0)
115 #define PRCI_PLLS_GEMGXLPLL (1 << 1)
116 #define PRCI_PLLS_DDRPLL (1 << 2)
117 #define PRCI_PLLS_HFPCLKPLL (1 << 3)
118 #define PRCI_PLLS_DVFSCOREPLL (1 << 4)
119 #define PRCI_PLLS_COREPLL (1 << 5)
121 // ===================================
122 // clk_mux_status register format
123 // ===================================
125 // read only register which is used to set some clock multiplex settings
126 // the value of this register depends on the state of pins connected to the FU740 SOC
127 // on the hifive-unmatched board the state of the pins is set by a hardware switch
128 #define PRCI_CLK_MUX_STATUS_CORECLKPLLSEL (1 << 0)
129 // 0 - HFCLK or CORECLK
131 #define PRCI_CLK_MUX_STATUS_TLCLKSEL (1 << 1)
134 #define PRCI_CLK_MUX_STATUS_RTCXSEL (1 << 2)
135 // 0 - use HFXCLK for RTC
136 // 1 - use RTCXALTCLKIN for RTC
137 #define PRCI_CLK_MUX_STATUS_DDRCTRLCLKSEL (1 << 3)
138 #define PRCI_CLK_MUX_STATUS_DDRPHYCLKSEL (1 << 4)
139 #define PRCI_CLK_MUX_STATUS_RESERVED (1 << 5)
140 #define PRCI_CLK_MUX_STATUS_GEMGXLCLKSEL (1 << 6)
141 #define PRCI_CLK_MUX_STATUS_MAINMEMCLKSEL (1 << 7)
143 // ===================================
144 // hfxosccfg register format
145 // ===================================
147 #define PRCI_HFXOSCCFG_HFXOSEN (1 << 30) // Crystal oscillator enable
148 // Note: I guess (it is not documented)
151 #define PRCI_HFXOSCCFG_HFXOSCRDY (1 << 31) // Crystal oscillator ready
153 struct pll_settings
{
154 unsigned int divr
:6; // divider before PLL loop (reference), equal to divr + 1
155 unsigned int divf
:9; // VCO feedback divider value, equal to 2 * (divf + 1)
156 unsigned int divq
:3; // divider after PLL loop, equal to 2^divq
157 // PLL filter range (TODO documentation is not really clear on how to set it)
158 unsigned int range
:3;
159 unsigned int bypass
:1; // probably used to bypass the PLL
160 // internal or external input path (internal = 1, external = 0)
161 //WARN this is only a guess since it is undocumented
162 unsigned int fsebypass
:1;
165 static void configure_pll(u32
*reg
, const struct pll_settings
*s
)
167 // Write the settings to the register
169 clrsetbits32(&c
, PRCI_PLLCFG_DIVR_MASK
170 | PRCI_PLLCFG_DIVF_MASK
171 | PRCI_PLLCFG_DIVQ_MASK
172 | PRCI_PLLCFG_RANGE_MASK
173 | PRCI_PLLCFG_BYPASS_MASK
174 | PRCI_PLLCFG_FSEBYPASS_MASK
,
175 (s
->divr
<< PRCI_PLLCFG_DIVR_SHIFT
)
176 | (s
->divf
<< PRCI_PLLCFG_DIVF_SHIFT
)
177 | (s
->divq
<< PRCI_PLLCFG_DIVQ_SHIFT
)
178 | (s
->range
<< PRCI_PLLCFG_RANGE_SHIFT
)
179 | (s
->bypass
<< PRCI_PLLCFG_BYPASS_SHIFT
)
180 | (s
->fsebypass
<< PRCI_PLLCFG_FSEBYPASS_SHIFT
));
184 while (!(read32(reg
) & PRCI_PLLCFG_LOCK_MASK
))
189 * Section 7.1 recommends a frequency of 1.0 GHz (up to 1.5 GHz is possible)
190 * Section 7.4.2 provides the necessary values
192 * COREPLL is set up for ~1 GHz output frequency.
193 * divr = 0 (x1), divf = 76 (x154) => (4004 MHz VCO), divq = 2 (/4 Output divider)
195 static const struct pll_settings corepll_settings
= {
201 .fsebypass
= 1, // external feedback mode is not supported
205 * Section 7.4.3: DDR and Ethernet Subsystem Clocking and Reset
207 * DDRPLL is set up for 933 MHz output frequency.
208 * divr = 0 (x1), divf = 71 (x144) => (3744 MHz VCO), divq = 2 (/4 output divider)
210 static const struct pll_settings ddrpll_settings
= {
216 .fsebypass
= 1, // external feedback mode is not supported
220 * GEMGXLPLL is set up for 125 MHz output frequency.
221 * divr = 0 (x1), divf = 76 (x154) => (4004 MHz VCO), divq = 5 (/32 output divider)
223 static const struct pll_settings gemgxlpll_settings
= {
229 .fsebypass
= 1, // external feedback mode is not supported
233 * HFPCLKPLL is set up for 520 MHz output frequency.
234 * TODO a lower value should also suffice as well as safe some power
235 * divr = 1 (/2), divf = 39 (x80) => (2080 MHz VCO), divq = 2 (/4 output divider)
237 static const struct pll_settings hfpclkpll_settings
= {
244 .fsebypass
= 1, // external feedback mode is not supported
248 * CLTXCLKPLL is set up for 520 MHz output frequency.
249 * divr = 1 (/2), divf = 122 (x154) => (4004 MHz VCO), divq = 2 (/4 output divider)
251 static const struct pll_settings cltxpll_settings
= {
257 .fsebypass
= 1, // external feedback mode is not supported
260 static void init_coreclk(void)
262 // we can't modify the coreclk PLL while we are running on it, so let coreclk devise
263 // its clock from hfclk before modifying PLL
264 clrsetbits32(&prci
->core_clk_sel_reg
, PRCI_CORECLKSEL_MASK
, PRCI_CORECLKSEL_HFCLK
);
266 // only configure pll if it is present
267 if (!(read32(&prci
->prci_plls
) & PRCI_PLLS_COREPLL
)) {
271 configure_pll(&prci
->core_pllcfg
, &corepll_settings
);
273 // switch coreclk multiplexer to use corepll as clock source again
274 clrsetbits32(&prci
->core_clk_sel_reg
, PRCI_CORECLKSEL_MASK
, PRCI_CORECLKSEL_CORECLKPLL
);
277 static void init_ddrclk(void)
279 // only configure pll if it is present
280 if (!(read32(&prci
->prci_plls
) & PRCI_PLLS_DDRPLL
)) {
284 // disable ddr clock output before reconfiguring the PLL
285 u32 cfg1
= read32(&prci
->ddr_plloutdiv
);
286 clrbits32(&cfg1
, PRCI_DDR_PLLOUTDIV_MASK
);
287 write32(&prci
->ddr_plloutdiv
, cfg1
);
289 configure_pll(&prci
->ddr_pllcfg
, &ddrpll_settings
);
291 // PLL is ready/locked so enable it (its gated)
292 setbits32(&cfg1
, PRCI_DDR_PLLOUTDIV_MASK
);
293 write32(&prci
->ddr_plloutdiv
, cfg1
);
296 static void init_gemgxlclk(void)
298 // only configure pll if it is present
299 if (!(read32(&prci
->prci_plls
) & PRCI_PLLS_GEMGXLPLL
)) {
303 // disable gemgxl clock output before reconfiguring the PLL
304 u32 cfg1
= read32(&prci
->gemgxl_plloutdiv
);
305 clrbits32(&cfg1
, PRCI_GEMGXL_PLLOUTDIV_MASK
);
306 write32(&prci
->gemgxl_plloutdiv
, cfg1
);
308 configure_pll(&prci
->gemgxl_pllcfg
, &gemgxlpll_settings
);
310 // PLL is ready/locked so enable it (its gated)
311 setbits32(&cfg1
, PRCI_GEMGXL_PLLOUTDIV_MASK
);
312 write32(&prci
->gemgxl_plloutdiv
, cfg1
);
316 * Configure High Frequency peripheral clock which is used by
317 * UART, SPI, GPIO, I2C and PWM subsystem
319 static void init_hfpclk(void)
321 // we can't modify the hfpclk PLL while we are running on it, so let pclk devise
322 // its clock from hfclk before modifying PLL
323 u32 hfpclksel
= read32(&prci
->hfpclkpllsel
);
324 hfpclksel
|= PRCI_HFPCLKSEL_HFCLK
;
325 write32(&prci
->hfpclkpllsel
, hfpclksel
);
327 configure_pll(&prci
->hfpclk_pllcfg
, &hfpclkpll_settings
);
329 // PLL is ready/locked so enable it (its gated)
330 u32 hfpclk_plloutdiv
= read32(&prci
->hfpclk_plloutdiv
);
331 hfpclk_plloutdiv
|= PRCI_HFPCLK_PLLOUTDIV_MASK
;
332 write32(&prci
->hfpclk_plloutdiv
, hfpclk_plloutdiv
);
336 // switch to using PLL for hfpclk
337 clrbits32(&prci
->hfpclkpllsel
, PRCI_HFPCLKSEL_MASK
);
342 static void reset_deassert(u8 reset_index
)
344 u32 device_reset
= read32(&prci
->devices_reset_n
);
345 device_reset
|= reset_index
;
346 write32(&prci
->devices_reset_n
, device_reset
);
349 static void init_cltx(void)
351 // disable hfpclkpll before configuring it
352 u32 cfg1
= read32(&prci
->cltx_plloutdiv
);
353 clrbits32(&cfg1
, PRCI_CLTX_PLLOUTDIV_MASK
);
354 write32(&prci
->cltx_plloutdiv
, cfg1
);
356 configure_pll(&prci
->cltx_pllcfg
, &cltxpll_settings
);
358 // PLL is ready/locked so enable it (its gated)
359 setbits32(&cfg1
, PRCI_CLTX_PLLOUTDIV_MASK
);
360 write32(&prci
->cltx_plloutdiv
, cfg1
);
362 // get chiplink out of reset
363 reset_deassert(PRCI_DEVICES_RESET_CLTX_RST
);
368 void clock_init(void)
370 // first configure the coreclk (used by HARTs) to get maximum speed early on
373 // put all devices in reset (e.g. DDR, ethernet, pcie) before configuring their clocks
374 write32(&prci
->devices_reset_n
, 0);
376 // initialize clock used by DDR subsystem
379 // get DDR controller out of reset
380 reset_deassert(PRCI_DEVICES_RESET_DDR_CTRL_RST
);
382 // wait at least one full DDR controller clock cycle
383 asm volatile ("fence");
385 // get DDR controller (register interface) out of reset
386 // get DDR subsystem PHY out of reset
387 reset_deassert(PRCI_DEVICES_RESET_DDR_AXI_RST
|
388 PRCI_DEVICES_RESET_DDR_AHB_RST
|
389 PRCI_DEVICES_RESET_DDR_PHY_RST
);
391 // we need to wait 256 full ddrctrl clock cycles until we can interact with the DDR subsystem
392 for (int i
= 0; i
< 256; i
++)
393 asm volatile ("nop");
395 if (read32(&prci
->prci_plls
) & PRCI_PLLS_HFPCLKPLL
) {
396 // set hfclk as reference for peripheral clock since we don't have the PLL
397 //clrsetbits32(&prci->hfpclkpllsel, PRCI_HFPCLKSEL_MASK, PRCI_HFPCLKSEL_HFCLK);
399 } else if (read32(&prci
->prci_plls
) & PRCI_PLLS_CLTXPLL
) {
400 // Note: this path has never been tested since the platforms tested with
401 // always have HFPCLKPLL
403 // get chiplink out of reset
404 reset_deassert(PRCI_DEVICES_RESET_CLTX_RST
);
407 // GEMGXL init VSC8541 PHY reset sequence;
408 gpio_set_direction(GEMGXL_RST
, GPIO_OUTPUT
);
409 gpio_set(GEMGXL_RST
, 1);
413 /* Reset PHY again to enter unmanaged mode */
414 gpio_set(GEMGXL_RST
, 0);
416 gpio_set(GEMGXL_RST
, 1);
421 // get ethernet out of reset
422 reset_deassert(PRCI_DEVICES_RESET_GEMGXL_RST
);
425 // get the peripheral clock frequency used by UART (probably also SPI, GPIO, I2C and PWM)
426 int clock_get_pclk(void)
428 u64 pclk
= FU740_HFCLK_FREQ
;
430 // check if hfpclkpll is present and
431 // check if hfpclkpll is selected in the multiplexer TODO
432 // check if hpfclkpll is enabled
433 if ((read32(&prci
->prci_plls
) & PRCI_PLLS_HFPCLKPLL
) &&
434 (read32(&prci
->hfpclk_plloutdiv
) & PRCI_HFPCLK_PLLOUTDIV_MASK
)) {
435 int hfpclk_pllcfg
= read32(&prci
->hfpclk_pllcfg
);
436 int divr
= (hfpclk_pllcfg
& PRCI_PLLCFG_DIVR_MASK
) >> PRCI_PLLCFG_DIVR_SHIFT
;
437 int divf
= (hfpclk_pllcfg
& PRCI_PLLCFG_DIVF_MASK
) >> PRCI_PLLCFG_DIVF_SHIFT
;
438 int divq
= (hfpclk_pllcfg
& PRCI_PLLCFG_DIVQ_MASK
) >> PRCI_PLLCFG_DIVQ_SHIFT
;
439 pclk
/= (divr
+ 1); // reference divider
440 pclk
*= (2 * (divf
+ 1)); // feedback divider
441 pclk
/= (1 << divq
); // output divider
444 // divider value before pclk seems to be (hfpclkdiv + 2). Not mentioned in fu740 manual though.
445 return pclk
/ (read32(&prci
->hfpclk_div_reg
) + 2);