blob: 419a0e2936d6a28fc82a46aa8c6670e126ca6375 [file] [log] [blame]
Maximilian Brune2ccb8e72024-01-14 21:59:27 +06001/* SPDX-License-Identifier: GPL-2.0-only */
2
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
5
6#include <console/console.h>
7#include <delay.h>
8#include <device/mmio.h>
9#include <soc/addressmap.h>
10#include <soc/clock.h>
11#include <soc/gpio.h>
12#include <gpio.h>
13#include <stdint.h>
14
15// Clock frequencies for the cores, ddr and the peripherals are all derived from the hfclk (high frequency clock) and it is always 26 MHz
16#define FU740_HFCLK_FREQ (26 * MHz)
17
18struct prci_ctlr {
19 u32 hfxosccfg; // offset 0x00
20 u32 core_pllcfg; // offset 0x04
21 u32 core_plloutdiv; // offset 0x08
22 u32 ddr_pllcfg; // offset 0x0c
23 u32 ddr_plloutdiv; // offset 0x10
24 u32 pcieaux_plloutdiv; // offset 0x14 (undocumented)
25 u32 reserved18; // offset 0x18
26 u32 gemgxl_pllcfg; // offset 0x1c
27 u32 gemgxl_plloutdiv; // offset 0x20
28 u32 core_clk_sel_reg; // offset 0x24
29 u32 devices_reset_n; // offset 0x28
30 u32 clk_mux_status; // offset 0x2C
31 u32 cltx_pllcfg; // offset 0x30 chiplink (undocumented)
32 u32 cltx_plloutdiv; // offset 0x34 chiplink (undocumented)
33 u32 dvfs_core_pllcfg; // offset 0x38
34 u32 dvfs_core_plloutdiv; // offset 0x3C
35 u32 corepllsel; // offset 0x40 (undocumented, but probably same as last gen)
36 u8 reserved44[12]; // offset 0x44
37 u32 hfpclk_pllcfg; // offset 0x50
38 u32 hfpclk_plloutdiv; // offset 0x54
39 u32 hfpclkpllsel; // offset 0x58 (undocumented, but probably same as last gen)
40 u32 hfpclk_div_reg; // offset 0x5C
41 u8 reserved60[128]; // offset 0x60
42 u32 prci_plls; // offset 0xE0
43 u8 reservedE4[12]; // offset 0xE4
44 u32 procmoncfg_core_clock; // offset 0xF0 (undocumented)
45} __packed;
46
47static struct prci_ctlr *prci = (void *)FU740_PRCI;
48
49// =================================
50// clock selections
51// =================================
52
53#define PRCI_COREPLLSEL_MASK 1
54#define PRCI_COREPLLSEL_COREPLL 0
55#define PRCI_COREPLLSEL_DVFSCOREPLL 1
56
57#define PRCI_CORECLKSEL_MASK 1
58#define PRCI_CORECLKSEL_CORECLKPLL 0
59#define PRCI_CORECLKSEL_HFCLK 1
60
61#define PRCI_HFPCLKSEL_MASK 1
62#define PRCI_HFPCLKSEL_PLL 0
63#define PRCI_HFPCLKSEL_HFCLK 1
64
65// ===================================
66// pllcfg register format is used by all PLLs
67// ===================================
68
69#define PRCI_PLLCFG_DIVR_SHIFT 0
70#define PRCI_PLLCFG_DIVF_SHIFT 6
71#define PRCI_PLLCFG_DIVQ_SHIFT 15
72#define PRCI_PLLCFG_RANGE_SHIFT 18
73#define PRCI_PLLCFG_BYPASS_SHIFT 24
74#define PRCI_PLLCFG_FSEBYPASS_SHIFT 25
75#define PRCI_PLLCFG_LOCK_SHIFT 31
76
77#define PRCI_PLLCFG_DIVR_MASK (0x03f << PRCI_PLLCFG_DIVR_SHIFT)
78#define PRCI_PLLCFG_DIVF_MASK (0x1ff << PRCI_PLLCFG_DIVF_SHIFT)
79#define PRCI_PLLCFG_DIVQ_MASK (0x007 << PRCI_PLLCFG_DIVQ_SHIFT)
80#define PRCI_PLLCFG_RANGE_MASK (0x007 << PRCI_PLLCFG_RANGE_SHIFT)
81#define PRCI_PLLCFG_BYPASS_MASK (0x001 << PRCI_PLLCFG_BYPASS_SHIFT)
82#define PRCI_PLLCFG_FSEBYPASS_MASK (0x001 << PRCI_PLLCFG_FSEBYPASS_SHIFT)
83#define PRCI_PLLCFG_LOCK_MASK (0x001 << PRCI_PLLCFG_LOCK_SHIFT)
84
85// ===================================
86// plloutdiv register formats
87// ===================================
88
89// registered are used to enable/disable PLLs
90#define PRCI_DVFSCORE_PLLOUTDIV_MASK (1 << 24) // Note: u-boot and fu740 manual differ here ...
91#define PRCI_HFPCLK_PLLOUTDIV_MASK (1 << 31) // Note: according to u-boot it is (1 << 24) but if I use that it gets stuck
92#define PRCI_DDR_PLLOUTDIV_MASK (1 << 31)
93#define PRCI_GEMGXL_PLLOUTDIV_MASK (1 << 31)
94#define PRCI_CLTX_PLLOUTDIV_MASK (1 << 24) // undocumented (chiplink tx)
95#define PRCI_PCIEAUX_PLLOUTDIV_MASK (1 << 0) // undocumented
96#define PRCI_CORE_PLLOUTDIV_MASK (1 << 31) // undocumented
97
98// ===================================
99// devicereset register formats
100// ===================================
101
102// used to get devices in or out of reset
103#define PRCI_DEVICES_RESET_DDR_CTRL_RST (1 << 0) // DDR Controller
104#define PRCI_DEVICES_RESET_DDR_AXI_RST (1 << 1) // DDR Controller AXI Interface
105#define PRCI_DEVICES_RESET_DDR_AHB_RST (1 << 2) // DDR Controller AHB Interface
106#define PRCI_DEVICES_RESET_DDR_PHY_RST (1 << 3) // DDR PHY
107#define PRCI_DEVICES_RESET_PCIEAUX_RST (1 << 4)
108#define PRCI_DEVICES_RESET_GEMGXL_RST (1 << 5) // Gigabit Ethernet Subsystem
109#define PRCI_DEVICES_RESET_CLTX_RST (1 << 6) // chiplink reset (undocumented)
110
111// ===================================
112// prci_plls register format
113// ===================================
114
115// used to check if certain PLLs are present in the SOC
116#define PRCI_PLLS_CLTXPLL (1 << 0)
117#define PRCI_PLLS_GEMGXLPLL (1 << 1)
118#define PRCI_PLLS_DDRPLL (1 << 2)
119#define PRCI_PLLS_HFPCLKPLL (1 << 3)
120#define PRCI_PLLS_DVFSCOREPLL (1 << 4)
121#define PRCI_PLLS_COREPLL (1 << 5)
122
123// ===================================
124// clk_mux_status register format
125// ===================================
126
127// read only register which is used to set some clock multiplex settings
128// the value of this register depends on the state of pins connected to the FU740 SOC
129// on the hifive-unmatched board the state of the pins is set by a hardware switch
130#define PRCI_CLK_MUX_STATUS_CORECLKPLLSEL (1 << 0)
131 // 0 - HFCLK or CORECLK
132 // 1 - only HFCLK
133#define PRCI_CLK_MUX_STATUS_TLCLKSEL (1 << 1)
134 // 0 - CORECLK/2
135 // 1 - CORECLK
136#define PRCI_CLK_MUX_STATUS_RTCXSEL (1 << 2)
137 // 0 - use HFXCLK for RTC
138 // 1 - use RTCXALTCLKIN for RTC
139#define PRCI_CLK_MUX_STATUS_DDRCTRLCLKSEL (1 << 3)
140#define PRCI_CLK_MUX_STATUS_DDRPHYCLKSEL (1 << 4)
141#define PRCI_CLK_MUX_STATUS_RESERVED (1 << 5)
142#define PRCI_CLK_MUX_STATUS_GEMGXLCLKSEL (1 << 6)
143#define PRCI_CLK_MUX_STATUS_MAINMEMCLKSEL (1 << 7)
144
145// ===================================
146// hfxosccfg register format
147// ===================================
148
149#define PRCI_HFXOSCCFG_HFXOSEN (1 << 30) // Crystal oscillator enable
150 // Note: I guess (it is not documented)
151 // 0 - XTAL PADS
152 // 1 - OSC PADS
153#define PRCI_HFXOSCCFG_HFXOSCRDY (1 << 31) // Crystal oscillator ready
154
155struct pll_settings {
156 unsigned int divr:6; // divider before PLL loop (reference), equal to divr + 1
157 unsigned int divf:9; // VCO feedback divider value, equal to 2 * (divf + 1)
158 unsigned int divq:3; // divider after PLL loop, equal to 2^divq
159 // PLL filter range (TODO documentation is not really clear on how to set it)
160 unsigned int range:3;
161 unsigned int bypass:1; // probably used to bypass the PLL
162 // internal or external input path (internal = 1, external = 0)
163 //WARN this is only a guess since it is undocumented
164 unsigned int fsebypass:1;
165};
166
167static void configure_pll(u32 *reg, const struct pll_settings *s)
168{
169 // Write the settings to the register
170 u32 c = read32(reg);
171 clrsetbits32(&c, PRCI_PLLCFG_DIVR_MASK
172 | PRCI_PLLCFG_DIVF_MASK
173 | PRCI_PLLCFG_DIVQ_MASK
174 | PRCI_PLLCFG_RANGE_MASK
175 | PRCI_PLLCFG_BYPASS_MASK
176 | PRCI_PLLCFG_FSEBYPASS_MASK,
177 (s->divr << PRCI_PLLCFG_DIVR_SHIFT)
178 | (s->divf << PRCI_PLLCFG_DIVF_SHIFT)
179 | (s->divq << PRCI_PLLCFG_DIVQ_SHIFT)
180 | (s->range << PRCI_PLLCFG_RANGE_SHIFT)
181 | (s->bypass << PRCI_PLLCFG_BYPASS_SHIFT)
182 | (s->fsebypass << PRCI_PLLCFG_FSEBYPASS_SHIFT));
183 write32(reg, c);
184
185 // Wait for PLL lock
186 while (!(read32(reg) & PRCI_PLLCFG_LOCK_MASK))
187 ;
188}
189
190/*
191 * Section 7.1 recommends a frequency of 1.0 GHz (up to 1.5 GHz is possible)
192 * Section 7.4.2 provides the necessary values
193 *
194 * COREPLL is set up for ~1 GHz output frequency.
195 * divr = 0 (x1), divf = 76 (x154) => (4004 MHz VCO), divq = 2 (/4 Output divider)
196 */
197static const struct pll_settings corepll_settings = {
198 .divr = 0,
199 .divf = 76,
200 .divq = 2,
201 .range = 4,
202 .bypass = 0,
203 .fsebypass = 1, // external feedback mode is not supported
204};
205
206/*
207 * Section 7.4.3: DDR and Ethernet Subsystem Clocking and Reset
208 *
209 * DDRPLL is set up for 933 MHz output frequency.
210 * divr = 0 (x1), divf = 71 (x144) => (3744 MHz VCO), divq = 2 (/4 output divider)
211 */
212static const struct pll_settings ddrpll_settings = {
213 .divr = 0,
214 .divf = 71,
215 .divq = 2,
216 .range = 4,
217 .bypass = 0,
218 .fsebypass = 1, // external feedback mode is not supported
219};
220
221/*
222 * GEMGXLPLL is set up for 125 MHz output frequency.
223 * divr = 0 (x1), divf = 76 (x154) => (4004 MHz VCO), divq = 5 (/32 output divider)
224 */
225static const struct pll_settings gemgxlpll_settings = {
226 .divr = 0,
227 .divf = 76,
228 .divq = 5,
229 .range = 4,
230 .bypass = 0,
231 .fsebypass = 1, // external feedback mode is not supported
232};
233
234/*
235 * HFPCLKPLL is set up for 520 MHz output frequency.
236 * TODO a lower value should also suffice as well as safe some power
237 * divr = 1 (/2), divf = 39 (x80) => (2080 MHz VCO), divq = 2 (/4 output divider)
238 */
239static const struct pll_settings hfpclkpll_settings = {
240 .divr = 1,
241 //.divf = 122,
242 .divf = 39,
243 .divq = 2,
244 .range = 4,
245 .bypass = 0,
246 .fsebypass = 1, // external feedback mode is not supported
247};
248
249/*
250 * CLTXCLKPLL is set up for 520 MHz output frequency.
251 * divr = 1 (/2), divf = 122 (x154) => (4004 MHz VCO), divq = 2 (/4 output divider)
252 */
253static const struct pll_settings cltxpll_settings = {
254 .divr = 1,
255 .divf = 39,
256 .divq = 2,
257 .range = 4,
258 .bypass = 0,
259 .fsebypass = 1, // external feedback mode is not supported
260};
261
262static void init_coreclk(void)
263{
264 // we can't modify the coreclk PLL while we are running on it, so let coreclk devise
265 // its clock from hfclk before modifying PLL
266 clrsetbits32(&prci->core_clk_sel_reg, PRCI_CORECLKSEL_MASK, PRCI_CORECLKSEL_HFCLK);
267
268 // only configure pll if it is present
269 if (!(read32(&prci->prci_plls) & PRCI_PLLS_COREPLL)) {
270 return;
271 }
272
273 configure_pll(&prci->core_pllcfg, &corepll_settings);
274
275 // switch coreclk multiplexer to use corepll as clock source again
276 clrsetbits32(&prci->core_clk_sel_reg, PRCI_CORECLKSEL_MASK, PRCI_CORECLKSEL_CORECLKPLL);
277}
278
279static void init_ddrclk(void)
280{
281 // only configure pll if it is present
282 if (!(read32(&prci->prci_plls) & PRCI_PLLS_DDRPLL)) {
283 return;
284 }
285
286 // disable ddr clock output before reconfiguring the PLL
287 u32 cfg1 = read32(&prci->ddr_plloutdiv);
288 clrbits32(&cfg1, PRCI_DDR_PLLOUTDIV_MASK);
289 write32(&prci->ddr_plloutdiv, cfg1);
290
291 configure_pll(&prci->ddr_pllcfg, &ddrpll_settings);
292
293 // PLL is ready/locked so enable it (its gated)
294 setbits32(&cfg1, PRCI_DDR_PLLOUTDIV_MASK);
295 write32(&prci->ddr_plloutdiv, cfg1);
296}
297
298static void init_gemgxlclk(void)
299{
300 // only configure pll if it is present
301 if (!(read32(&prci->prci_plls) & PRCI_PLLS_GEMGXLPLL)) {
302 return;
303 }
304
305 // disable gemgxl clock output before reconfiguring the PLL
306 u32 cfg1 = read32(&prci->gemgxl_plloutdiv);
307 clrbits32(&cfg1, PRCI_GEMGXL_PLLOUTDIV_MASK);
308 write32(&prci->gemgxl_plloutdiv, cfg1);
309
310 configure_pll(&prci->gemgxl_pllcfg, &gemgxlpll_settings);
311
312 // PLL is ready/locked so enable it (its gated)
313 setbits32(&cfg1, PRCI_GEMGXL_PLLOUTDIV_MASK);
314 write32(&prci->gemgxl_plloutdiv, cfg1);
315}
316
317/*
318 * Configure High Frequency peripheral clock which is used by
319 * UART, SPI, GPIO, I2C and PWM subsystem
320 */
321static void init_hfpclk(void)
322{
323 // we can't modify the hfpclk PLL while we are running on it, so let pclk devise
324 // its clock from hfclk before modifying PLL
325 u32 hfpclksel = read32(&prci->hfpclkpllsel);
326 hfpclksel |= PRCI_HFPCLKSEL_HFCLK;
327 write32(&prci->hfpclkpllsel, hfpclksel);
328
329 configure_pll(&prci->hfpclk_pllcfg, &hfpclkpll_settings);
330
331 // PLL is ready/locked so enable it (its gated)
332 u32 hfpclk_plloutdiv = read32(&prci->hfpclk_plloutdiv);
333 hfpclk_plloutdiv |= PRCI_HFPCLK_PLLOUTDIV_MASK;
334 write32(&prci->hfpclk_plloutdiv, hfpclk_plloutdiv);
335
336 mdelay(1);
337
338 // switch to using PLL for hfpclk
339 clrbits32(&prci->hfpclkpllsel, PRCI_HFPCLKSEL_MASK);
340
341 udelay(70);
342}
343
344static void reset_deassert(u8 reset_index)
345{
346 u32 device_reset = read32(&prci->devices_reset_n);
347 device_reset |= reset_index;
348 write32(&prci->devices_reset_n, device_reset);
349}
350
351static void init_cltx(void)
352{
353 // disable hfpclkpll before configuring it
354 u32 cfg1 = read32(&prci->cltx_plloutdiv);
355 clrbits32(&cfg1, PRCI_CLTX_PLLOUTDIV_MASK);
356 write32(&prci->cltx_plloutdiv, cfg1);
357
358 configure_pll(&prci->cltx_pllcfg, &cltxpll_settings);
359
360 // PLL is ready/locked so enable it (its gated)
361 setbits32(&cfg1, PRCI_CLTX_PLLOUTDIV_MASK);
362 write32(&prci->cltx_plloutdiv, cfg1);
363
364 // get chiplink out of reset
365 reset_deassert(PRCI_DEVICES_RESET_CLTX_RST);
366
367 udelay(70);
368}
369
370void clock_init(void)
371{
372 // first configure the coreclk (used by HARTs) to get maximum speed early on
373 init_coreclk();
374
375 // put all devices in reset (e.g. DDR, ethernet, pcie) before configuring their clocks
376 write32(&prci->devices_reset_n, 0);
377
378 // initialize clock used by DDR subsystem
379 init_ddrclk();
380
381 // get DDR controller out of reset
382 reset_deassert(PRCI_DEVICES_RESET_DDR_CTRL_RST);
383
384 // wait at least one full DDR controller clock cycle
385 asm volatile ("fence");
386
387 // get DDR controller (register interface) out of reset
388 // get DDR subsystem PHY out of reset
389 reset_deassert(PRCI_DEVICES_RESET_DDR_AXI_RST |
390 PRCI_DEVICES_RESET_DDR_AHB_RST |
391 PRCI_DEVICES_RESET_DDR_PHY_RST);
392
393 // we need to wait 256 full ddrctrl clock cycles until we can interact with the DDR subsystem
394 for (int i = 0; i < 256; i++)
395 asm volatile ("nop");
396
397 if (read32(&prci->prci_plls) & PRCI_PLLS_HFPCLKPLL) {
398 // set hfclk as reference for peripheral clock since we don't have the PLL
399 //clrsetbits32(&prci->hfpclkpllsel, PRCI_HFPCLKSEL_MASK, PRCI_HFPCLKSEL_HFCLK);
400 init_hfpclk();
401 } else if (read32(&prci->prci_plls) & PRCI_PLLS_CLTXPLL) {
402 // Note: this path has never been tested since the platforms tested with
403 // always have HFPCLKPLL
404 init_cltx();
405 // get chiplink out of reset
406 reset_deassert(PRCI_DEVICES_RESET_CLTX_RST);
407 }
408
409 // GEMGXL init VSC8541 PHY reset sequence;
410 gpio_set_direction(GEMGXL_RST, GPIO_OUTPUT);
411 gpio_set(GEMGXL_RST, 1);
412
413 udelay(1);
414
415 /* Reset PHY again to enter unmanaged mode */
416 gpio_set(GEMGXL_RST, 0);
417 udelay(1);
418 gpio_set(GEMGXL_RST, 1);
419 mdelay(15);
420
421 init_gemgxlclk();
422
423 // get ethernet out of reset
424 reset_deassert(PRCI_DEVICES_RESET_GEMGXL_RST);
425}
426
427// get the peripheral clock frequency used by UART (probably also SPI, GPIO, I2C and PWM)
428int clock_get_pclk(void)
429{
430 u64 pclk = FU740_HFCLK_FREQ;
431
432 // check if hfpclkpll is present and
433 // check if hfpclkpll is selected in the multiplexer TODO
434 // check if hpfclkpll is enabled
435 if ((read32(&prci->prci_plls) & PRCI_PLLS_HFPCLKPLL) &&
436 (read32(&prci->hfpclk_plloutdiv) & PRCI_HFPCLK_PLLOUTDIV_MASK)) {
437 int hfpclk_pllcfg = read32(&prci->hfpclk_pllcfg);
438 int divr = (hfpclk_pllcfg & PRCI_PLLCFG_DIVR_MASK) >> PRCI_PLLCFG_DIVR_SHIFT;
439 int divf = (hfpclk_pllcfg & PRCI_PLLCFG_DIVF_MASK) >> PRCI_PLLCFG_DIVF_SHIFT;
440 int divq = (hfpclk_pllcfg & PRCI_PLLCFG_DIVQ_MASK) >> PRCI_PLLCFG_DIVQ_SHIFT;
441 pclk /= (divr + 1); // reference divider
442 pclk *= (2 * (divf + 1)); // feedback divider
443 pclk /= (1 << divq); // output divider
444 }
445
446 // divider value before pclk seems to be (hfpclkdiv + 2). Not mentioned in fu740 manual though.
447 return pclk / (read32(&prci->hfpclk_div_reg) + 2);
448}