blob: 977f938eb443ff4f45a10c402d8c11b031b9a898 [file] [log] [blame]
Angel Pons5de47d02020-04-04 18:51:30 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Philipp Hug3d398ad2018-07-07 14:24:53 +02002
Kyösti Mälkki13f66502019-03-03 08:01:05 +02003#include <device/mmio.h>
Philipp Hug3d398ad2018-07-07 14:24:53 +02004#include <console/console.h>
5#include <soc/clock.h>
6#include <soc/addressmap.h>
Philipp Hug3d398ad2018-07-07 14:24:53 +02007#include <stdint.h>
8
Jonathan Neuschäfera09c2e12018-12-01 16:37:00 +01009// 33.33 Mhz after reset
10#define FU540_BASE_FQY 33330
11
Philipp Hug3d398ad2018-07-07 14:24:53 +020012struct prci_ctlr {
13 u32 hfxosccfg; /* offset 0x00 */
14 u32 corepllcfg0; /* offset 0x04 */
15 u32 reserved08; /* offset 0x08 */
16 u32 ddrpllcfg0; /* offset 0x0c */
17 u32 ddrpllcfg1; /* offset 0x10 */
18 u32 reserved14; /* offset 0x14 */
19 u32 reserved18; /* offset 0x18 */
20 u32 gemgxlpllcfg0; /* offset 0x1c */
21 u32 gemgxlpllcfg1; /* offset 0x20 */
22 u32 coreclksel; /* offset 0x24 */
23 u32 devicesresetreg; /* offset 0x28 */
24};
25
26static struct prci_ctlr *prci = (void *)FU540_PRCI;
27
28#define PRCI_CORECLK_MASK 1
29#define PRCI_CORECLK_CORE_PLL 0
30#define PRCI_CORECLK_HFCLK 1
31
Jonathan Neuschäfera09c2e12018-12-01 16:37:00 +010032#define PRCI_PLLCFG_LOCK (1u << 31)
33#define PRCI_PLLCFG_DIVR_SHIFT 0
34#define PRCI_PLLCFG_DIVF_SHIFT 6
35#define PRCI_PLLCFG_DIVQ_SHIFT 15
36#define PRCI_PLLCFG_RANGE_SHIFT 18
37#define PRCI_PLLCFG_BYPASS_SHIFT 24
38#define PRCI_PLLCFG_FSE_SHIFT 25
39#define PRCI_PLLCFG_DIVR_MASK (0x03f << PRCI_PLLCFG_DIVR_SHIFT)
40#define PRCI_PLLCFG_DIVF_MASK (0x1ff << PRCI_PLLCFG_DIVF_SHIFT)
41#define PRCI_PLLCFG_DIVQ_MASK (0x007 << PRCI_PLLCFG_DIVQ_SHIFT)
42#define PRCI_PLLCFG_RANGE_MASK (0x07 << PRCI_PLLCFG_RANGE_SHIFT)
43#define PRCI_PLLCFG_BYPASS_MASK (0x1 << PRCI_PLLCFG_BYPASS_SHIFT)
44#define PRCI_PLLCFG_FSE_MASK (0x1 << PRCI_PLLCFG_FSE_SHIFT)
Philipp Hug3d398ad2018-07-07 14:24:53 +020045
46#define PRCI_DDRPLLCFG1_MASK (1u << 31)
47
Xiang Wang313d53f2019-01-24 14:59:58 +080048#define PRCI_GEMGXLPPLCFG1_MASK (1u << 31)
49
Philipp Hug3d398ad2018-07-07 14:24:53 +020050#define PRCI_CORECLKSEL_CORECLKSEL 1
51
Philipp Hugbdd866e2018-09-10 17:35:45 +020052#define PRCI_DEVICESRESET_DDR_CTRL_RST_N(x) (((x) & 0x1) << 0)
53#define PRCI_DEVICESRESET_DDR_AXI_RST_N(x) (((x) & 0x1) << 1)
54#define PRCI_DEVICESRESET_DDR_AHB_RST_N(x) (((x) & 0x1) << 2)
55#define PRCI_DEVICESRESET_DDR_PHY_RST_N(x) (((x) & 0x1) << 3)
56#define PRCI_DEVICESRESET_GEMGXL_RST_N(x) (((x) & 0x1) << 5)
57
Jonathan Neuschäfera09c2e12018-12-01 16:37:00 +010058/* Clock initialization should only be done in romstage. */
59#if ENV_ROMSTAGE
60struct pll_settings {
61 unsigned int divr:6;
62 unsigned int divf:9;
63 unsigned int divq:3;
64 unsigned int range:3;
65 unsigned int bypass:1;
66 unsigned int fse:1;
67};
68
69static void configure_pll(u32 *reg, const struct pll_settings *s)
70{
71 // Write the settings to the register
72 u32 c = read32(reg);
Julius Werner55009af2019-12-02 22:03:27 -080073 clrsetbits32(&c, PRCI_PLLCFG_DIVR_MASK
Jonathan Neuschäfera09c2e12018-12-01 16:37:00 +010074 | PRCI_PLLCFG_DIVF_MASK | PRCI_PLLCFG_DIVQ_MASK
75 | PRCI_PLLCFG_RANGE_MASK | PRCI_PLLCFG_BYPASS_MASK
76 | PRCI_PLLCFG_FSE_MASK,
77 (s->divr << PRCI_PLLCFG_DIVR_SHIFT)
78 | (s->divf << PRCI_PLLCFG_DIVF_SHIFT)
79 | (s->divq << PRCI_PLLCFG_DIVQ_SHIFT)
80 | (s->range << PRCI_PLLCFG_RANGE_SHIFT)
81 | (s->bypass << PRCI_PLLCFG_BYPASS_SHIFT)
82 | (s->fse << PRCI_PLLCFG_FSE_SHIFT));
83 write32(reg, c);
84
85 // Wait for PLL lock
86 while (!(read32(reg) & PRCI_PLLCFG_LOCK))
87 ; /* TODO: implement a timeout */
88}
89
Philipp Hug3d398ad2018-07-07 14:24:53 +020090/*
91 * Set coreclk according to the SiFive FU540-C000 Manual
92 * https://www.sifive.com/documentation/chips/freedom-u540-c000-manual/
93 *
Elyes HAOUAS1b296ee2020-02-19 20:48:29 +010094 * Section 7.1 recommends a frequency of 1.0 GHz (up to 1.5 GHz is possible)
Philipp Hug3d398ad2018-07-07 14:24:53 +020095 *
96 * Section 7.4.2 provides the necessary values:
97 * For example, to setup COREPLL for 1 GHz operation, program divr = 0 (x1),
98 * divf = 59 (4000 MHz VCO), divq = 2 (/4 Output divider)
99 */
Jonathan Neuschäfera09c2e12018-12-01 16:37:00 +0100100static const struct pll_settings corepll_settings = {
101 .divr = 0,
102 .divf = 59,
103 .divq = 2,
104 .range = 4,
105 .bypass = 0,
106 .fse = 1,
107};
Philipp Hug3d398ad2018-07-07 14:24:53 +0200108
109/*
110 * Section 7.4.3: DDR and Ethernet Subsystem Clocking and Reset
Philipp Hug18764a32018-09-08 19:48:19 +0200111 *
112 * Unfortunately the documentation example doesn't match the HiFive
113 * Unleashed board settings.
114 * Configuration values taken from SiFive FSBL:
115 * https://github.com/sifive/freedom-u540-c000-bootloader/blob/master/fsbl/main.c
116 *
117 * DDRPLL is set up for 933 MHz output frequency.
118 * divr = 0, divf = 55 (3730 MHz VCO), divq = 2
119 *
Philipp Hug3d398ad2018-07-07 14:24:53 +0200120 * GEMGXLPLL is set up for 125 MHz output frequency.
Philipp Hug18764a32018-09-08 19:48:19 +0200121 * divr = 0, divf = 59 (4000 MHz VCO), divq = 5
Philipp Hug3d398ad2018-07-07 14:24:53 +0200122 */
Jonathan Neuschäfera09c2e12018-12-01 16:37:00 +0100123static const struct pll_settings ddrpll_settings = {
124 .divr = 0,
125 .divf = 55,
126 .divq = 2,
127 .range = 4,
128 .bypass = 0,
129 .fse = 1,
130};
Philipp Hug3d398ad2018-07-07 14:24:53 +0200131
Xiang Wang313d53f2019-01-24 14:59:58 +0800132static const struct pll_settings gemgxlpll_settings = {
133 .divr = 0,
134 .divf = 59,
135 .divq = 5,
136 .range = 4,
137 .bypass = 0,
138 .fse = 1,
139};
140
Philipp Hug3d398ad2018-07-07 14:24:53 +0200141static void init_coreclk(void)
142{
143 // switch coreclk to input reference frequency before modifying PLL
Julius Werner55009af2019-12-02 22:03:27 -0800144 clrsetbits32(&prci->coreclksel, PRCI_CORECLK_MASK,
Philipp Hug3d398ad2018-07-07 14:24:53 +0200145 PRCI_CORECLK_HFCLK);
146
Jonathan Neuschäfera09c2e12018-12-01 16:37:00 +0100147 configure_pll(&prci->corepllcfg0, &corepll_settings);
Philipp Hug3d398ad2018-07-07 14:24:53 +0200148
149 // switch coreclk to use corepll
Julius Werner55009af2019-12-02 22:03:27 -0800150 clrsetbits32(&prci->coreclksel, PRCI_CORECLK_MASK,
Philipp Hug3d398ad2018-07-07 14:24:53 +0200151 PRCI_CORECLK_CORE_PLL);
152}
153
154static void init_pll_ddr(void)
155{
156 // disable ddr clock output before reconfiguring the PLL
157 u32 cfg1 = read32(&prci->ddrpllcfg1);
Julius Werner55009af2019-12-02 22:03:27 -0800158 clrbits32(&cfg1, PRCI_DDRPLLCFG1_MASK);
Philipp Hug3d398ad2018-07-07 14:24:53 +0200159 write32(&prci->ddrpllcfg1, cfg1);
160
Jonathan Neuschäfera09c2e12018-12-01 16:37:00 +0100161 configure_pll(&prci->ddrpllcfg0, &ddrpll_settings);
Philipp Hug3d398ad2018-07-07 14:24:53 +0200162
163 // enable ddr clock output
Julius Werner55009af2019-12-02 22:03:27 -0800164 setbits32(&cfg1, PRCI_DDRPLLCFG1_MASK);
Philipp Hug3d398ad2018-07-07 14:24:53 +0200165 write32(&prci->ddrpllcfg1, cfg1);
166}
167
Xiang Wang313d53f2019-01-24 14:59:58 +0800168static void init_gemgxlclk(void)
169{
170 u32 cfg1 = read32(&prci->gemgxlpllcfg1);
Julius Werner55009af2019-12-02 22:03:27 -0800171 clrbits32(&cfg1, PRCI_GEMGXLPPLCFG1_MASK);
Xiang Wang313d53f2019-01-24 14:59:58 +0800172 write32(&prci->gemgxlpllcfg1, cfg1);
173
174 configure_pll(&prci->gemgxlpllcfg0, &gemgxlpll_settings);
175
Julius Werner55009af2019-12-02 22:03:27 -0800176 setbits32(&cfg1, PRCI_GEMGXLPPLCFG1_MASK);
Xiang Wang313d53f2019-01-24 14:59:58 +0800177 write32(&prci->gemgxlpllcfg1, cfg1);
178}
179
Philipp Hug374d9922018-09-13 22:16:36 +0200180#define FU540_UART_DEVICES 2
181#define FU540_UART_REG_DIV 0x18
182#define FU540_UART_DIV_VAL 4
183
184#define FU540_SPI_DIV 0x00
185#define FU540_SPI_DIV_VAL 4
186
Philipp Hug374d9922018-09-13 22:16:36 +0200187static void update_peripheral_clock_dividers(void)
Philipp Hug3d398ad2018-07-07 14:24:53 +0200188{
Philipp Hug374d9922018-09-13 22:16:36 +0200189 write32((uint32_t *)(FU540_QSPI0 + FU540_SPI_DIV), FU540_SPI_DIV_VAL);
190 write32((uint32_t *)(FU540_QSPI1 + FU540_SPI_DIV), FU540_SPI_DIV_VAL);
191 write32((uint32_t *)(FU540_QSPI2 + FU540_SPI_DIV), FU540_SPI_DIV_VAL);
Philipp Hug3d398ad2018-07-07 14:24:53 +0200192
Philipp Hug374d9922018-09-13 22:16:36 +0200193 for (size_t i = 0; i < FU540_UART_DEVICES; i++)
194 write32((uint32_t *)(FU540_UART(i) + FU540_UART_REG_DIV), FU540_UART_DIV_VAL);
Philipp Hug3d398ad2018-07-07 14:24:53 +0200195}
196
197void clock_init(void)
198{
Philipp Hug374d9922018-09-13 22:16:36 +0200199 /*
200 * Update the peripheral clock dividers of UART, SPI and I2C to safe
201 * values as we can't put them in reset before changing frequency.
202 */
203 update_peripheral_clock_dividers();
204
Philipp Hug3d398ad2018-07-07 14:24:53 +0200205 init_coreclk();
206
207 // put DDR and ethernet in reset
208 write32(&prci->devicesresetreg, 0);
Philipp Hugbdd866e2018-09-10 17:35:45 +0200209
Philipp Hug3d398ad2018-07-07 14:24:53 +0200210 init_pll_ddr();
Philipp Hugbdd866e2018-09-10 17:35:45 +0200211
212 // The following code and its comments is mostly derived from the SiFive
213 // u540 bootloader.
214 // https://github.com/sifive/freedom-u540-c000-bootloader
215
216 // get DDR out of reset
217 write32(&prci->devicesresetreg, PRCI_DEVICESRESET_DDR_CTRL_RST_N(1));
218
219 // HACK to get the '1 full controller clock cycle'.
220 asm volatile ("fence");
221
222 // get DDR out of reset
223 write32(&prci->devicesresetreg,
224 PRCI_DEVICESRESET_DDR_CTRL_RST_N(1) |
225 PRCI_DEVICESRESET_DDR_AXI_RST_N(1) |
226 PRCI_DEVICESRESET_DDR_AHB_RST_N(1) |
227 PRCI_DEVICESRESET_DDR_PHY_RST_N(1));
228
229 // HACK to get the '1 full controller clock cycle'.
230 asm volatile ("fence");
231
232 // These take like 16 cycles to actually propagate. We can't go sending
233 // stuff before they come out of reset. So wait.
234 // TODO: Add a register to read the current reset states, or DDR Control
235 // device?
236 for (int i = 0; i < 256; i++)
237 asm volatile ("nop");
Xiang Wang313d53f2019-01-24 14:59:58 +0800238
239 init_gemgxlclk();
240
241 write32(&prci->devicesresetreg,
242 PRCI_DEVICESRESET_DDR_CTRL_RST_N(1) |
243 PRCI_DEVICESRESET_DDR_AXI_RST_N(1) |
244 PRCI_DEVICESRESET_DDR_AHB_RST_N(1) |
245 PRCI_DEVICESRESET_DDR_PHY_RST_N(1) |
246 PRCI_DEVICESRESET_GEMGXL_RST_N(1));
247
248 asm volatile ("fence");
Philipp Hug3d398ad2018-07-07 14:24:53 +0200249}
Jonathan Neuschäfer8ac6a192018-09-20 16:55:33 +0200250#endif /* ENV_ROMSTAGE */
Philipp Hug374d9922018-09-13 22:16:36 +0200251
Jonathan Neuschäfer90fd0722018-10-29 14:25:05 +0100252/* Get the core clock's frequency, in KHz */
Philipp Hug374d9922018-09-13 22:16:36 +0200253int clock_get_coreclk_khz(void)
254{
255 if (read32(&prci->coreclksel) & PRCI_CORECLK_MASK)
256 return FU540_BASE_FQY;
257
258 u32 cfg = read32(&prci->corepllcfg0);
Jonathan Neuschäfera09c2e12018-12-01 16:37:00 +0100259 u32 divr = (cfg & PRCI_PLLCFG_DIVR_MASK)
260 >> PRCI_PLLCFG_DIVR_SHIFT;
261 u32 divf = (cfg & PRCI_PLLCFG_DIVF_MASK)
262 >> PRCI_PLLCFG_DIVF_SHIFT;
263 u32 divq = (cfg & PRCI_PLLCFG_DIVQ_MASK)
264 >> PRCI_PLLCFG_DIVQ_SHIFT;
Philipp Hug374d9922018-09-13 22:16:36 +0200265
266 printk(BIOS_SPEW, "clk: r=%d f=%d q=%d\n", divr, divf, divq);
267 return FU540_BASE_FQY
268 * 2 * (divf + 1)
269 / (divr + 1)
270 / (1ul << divq);
271}
Jonathan Neuschäfer97ca02c2018-10-29 14:32:49 +0100272
273/* Get the TileLink clock's frequency, in KHz */
274int clock_get_tlclk_khz(void)
275{
276 /*
277 * The TileLink bus and most peripherals use tlclk, which is coreclk/2,
278 * as input.
279 */
280
281 return clock_get_coreclk_khz() / 2;
282}